从零开始掌握 Web 安全:2026 年网络安全工程师必须掌握的漏洞挖掘技术
Web 安全一直是网络安全学习和实际安全工作中的核心方向之一。对于刚刚进入网络安全行业的同学来说,真正困难的往往不是记住某个漏洞名称,而是理解漏洞产生的根本原因、建立完整的漏洞分析思路,并能够通过代码和工具进行验证。
本文将以 Web 应用安全为主线,从 HTTP 请求开始,一步步分析 SQL 注入、XSS、CSRF、文件上传等常见漏洞,同时结合 Python、JavaScript、SQL、Shell 等代码示例,帮助初学者建立一套完整的 Web 安全知识体系。
一、为什么 Web 安全一直是网络安全的重要方向?
随着企业业务逐渐从传统桌面软件迁移到 Web 平台,Web 应用已经成为企业最主要的业务入口之一。
现在我们访问的电商平台、管理后台、视频网站、在线教育平台、企业 OA、ERP、CRM,甚至很多内部办公系统,本质上都属于 Web 应用。
Web 应用带来的优势非常明显:
-
用户无需安装复杂的软件
-
浏览器即可访问
-
系统可以快速迭代
-
可以方便接入第三方服务
-
能够支持大规模用户访问
但与此同时,Web 应用也增加了攻击面。
一个看似普通的登录接口:
POST /login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
username=test&password=123456
实际上可能涉及:
浏览器
↓
CDN
↓
WAF
↓
Web服务器
↓
应用程序
↓
数据库
↓
缓存系统
↓
内部服务
只要其中任意一个环节存在安全问题,都可能进一步影响整个业务系统。
所以学习 Web 安全,不能只停留在“背漏洞”。
真正需要掌握的是:
请求是怎么产生的?
参数是怎么传递的?
程序是怎么处理参数的?
数据最终进入了哪里?
在什么地方发生了危险的数据流?
这才是漏洞挖掘真正的核心。
二、学习 Web 安全之前,首先要理解 HTTP
很多刚入门网络安全的同学一上来就学习 SQL 注入、XSS、Burp Suite,结果学了很久仍然感觉非常混乱。
原因很简单:
HTTP 基础不扎实。
Web 安全的很多问题,本质上都是围绕 HTTP 请求展开的。
一次典型的请求:
GET /user?id=1001 HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html
Cookie: session=abcdef123456
服务器返回:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
<html>
<body>
<h1>Hello User</h1>
</body>
</html>
这里面有几个非常重要的概念。
1. 请求方法
常见方法包括:
GET
POST
PUT
DELETE
PATCH
OPTIONS
HEAD
安全测试中最常接触的是 GET 和 POST。
例如:
GET /search?keyword=hello HTTP/1.1
参数出现在 URL 中。
而:
POST /login HTTP/1.1
username=admin&password=123456
参数出现在请求体中。
2. Cookie
Cookie 经常用于保存会话信息。
例如:
Cookie: sessionid=8a9f7d6c5b4a
如果应用没有正确保护 Session,就可能产生会话安全问题。
例如:
-
Session 固定
-
会话劫持
-
Cookie 泄露
-
Cookie 未设置安全属性
比较重要的 Cookie 属性:
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax
其中:
HttpOnly
可以降低客户端 JavaScript 读取 Cookie 的风险。
Secure
要求 Cookie 通过 HTTPS 发送。
SameSite
可以降低部分跨站请求风险。
三、Web 漏洞的本质:危险数据流
理解这一点之后,很多漏洞都会变得非常简单。
例如:
用户输入
↓
HTTP参数
↓
应用程序
↓
危险函数
↓
数据库 / 浏览器 / 操作系统
如果开发人员没有对数据进行正确校验,就可能产生安全问题。
例如:
username = request.args.get("username")
sql = "SELECT * FROM users WHERE username='" + username + "'"
问题就在这里:
用户控制的数据直接进入了 SQL 语句。
这就是经典的 SQL 注入风险。
因此在漏洞挖掘过程中,一个非常重要的方法就是:
找用户可控输入,再追踪输入最终流向。
这也是所谓的数据流分析。
四、SQL 注入到底是怎么产生的?
SQL 注入是 Web 安全中最经典的漏洞之一。
假设后台代码:
username = request.form["username"]
sql = "SELECT * FROM users WHERE username = '" + username + "'"
正常情况下:
username = admin
最终 SQL:
SELECT * FROM users WHERE username = 'admin';
如果程序直接拼接字符串,就可能导致 SQL 语句结构被用户影响。
因此不要简单理解 SQL 注入为:
“在输入框里面加特殊字符。”
真正的问题是:
用户输入被当成了 SQL 代码的一部分。
1. 安全写法:参数化查询
正确做法应该使用参数化查询。
例如 Python:
import sqlite3
conn = sqlite3.connect("demo.db")
username = request.form["username"]
cursor = conn.cursor()
cursor.execute(
"SELECT * FROM users WHERE username = ?",
(username,)
)
result = cursor.fetchall()
此时用户输入的数据不会直接参与 SQL 语句结构解析。
这就是为什么在实际开发中:
参数化查询是防御 SQL 注入的核心措施之一。
五、如何进行 SQL 注入漏洞排查?
在授权测试环境中,可以重点观察以下接口:
/login
/search
/product
/article
/user
/order
/comment
尤其是参数:
id
uid
user
username
keyword
search
sort
page
category
例如:
GET /product?id=1001 HTTP/1.1
安全测试过程中可以建立这样一个思维:
参数 id
↓
是否进入数据库查询?
↓
是否字符串拼接?
↓
是否参数化?
↓
异常输入是否被处理?
如果系统返回数据库错误,例如:
SQL syntax error
Database exception
SQLite error
MySQL error
PostgreSQL error
就应该进一步检查后台查询逻辑。
六、不要只看报错,更要理解程序逻辑
现代 Web 应用通常不会直接把数据库错误暴露给用户。
例如:
try:
cursor.execute(sql)
except Exception:
return "Request failed"
前端只看到:
Request failed
这并不代表不存在漏洞。
因为:
没有报错
≠
没有漏洞
很多时候需要结合:
-
响应时间
-
状态码
-
页面内容
-
数据变化
-
日志
-
源代码
-
API 行为
进行综合判断。
七、XSS:为什么浏览器会执行用户输入?
XSS,也就是跨站脚本攻击,同样是 Web 安全中的经典问题。
最简单的危险场景:
const keyword = getParameter("keyword");
document.write(keyword);
假设:
keyword = 用户输入
如果应用没有进行正确输出编码,就可能让浏览器把用户输入解释为 HTML 或 JavaScript。
例如一个危险的页面:
<div id="result"></div>
<script>
const data = location.search;
document.getElementById("result").innerHTML = data;
</script>
这里最大的风险在于:
innerHTML
它会把字符串当作 HTML 进行解析。
因此开发过程中,更安全的做法通常是:
const result = document.getElementById("result");
result.textContent = data;
区别就在于:
innerHTML
↓
解析 HTML
textContent
↓
当作普通文本
八、XSS 常见类型
XSS 通常可以分为三个主要方向。
1. 反射型 XSS
特点是:
用户输入
↓
服务器处理
↓
响应页面
↓
浏览器执行
常见于:
搜索页面
错误页面
参数回显
跳转页面
2. 存储型 XSS
用户输入的数据被保存到数据库:
用户评论
↓
数据库
↓
后台读取
↓
网页展示
↓
浏览器解析
这种类型影响范围往往更大。
例如:
评论内容
昵称
个人签名
文章标题
站内消息
都可能成为测试入口。
3. DOM 型 XSS
主要发生在前端 JavaScript 中。
例如:
const content = location.hash.substring(1);
document.getElementById("output").innerHTML = content;
此时服务器甚至不需要参与。
因此前端代码审计也越来越重要。
九、CSRF:为什么用户什么都没点,却完成了操作?
CSRF 可以理解成:
利用受害者当前已经登录的身份,让浏览器向目标系统发起非预期请求。
例如某后台存在修改邮箱接口:
POST /change-email HTTP/1.1
email=test@example.com
正常情况下:
用户登录
↓
浏览器保存 Cookie
↓
用户访问账户设置
↓
修改邮箱
但如果系统没有 CSRF 防护,攻击者可能诱导浏览器发起类似请求。
这里的关键点并不是“伪造 Cookie”。
而是:
浏览器可能会自动携带当前站点的身份凭证。
十、CSRF 的主要防御方法
最经典的方法是:
CSRF Token
服务端生成随机 Token:
import secrets
csrf_token = secrets.token_hex(32)
print(csrf_token)
然后页面中携带:
<input
type="hidden"
name="csrf_token"
value="随机Token">
服务器收到请求之后进行验证:
if request.form["csrf_token"] != session["csrf_token"]:
return "Invalid request", 403
此外,还应该合理使用:
SameSite Cookie
Origin 校验
Referer 校验
身份认证
权限验证
十一、文件上传漏洞为什么危险?
很多网站都存在:
头像上传
图片上传
附件上传
简历上传
文档上传
视频上传
最常见的错误思路:
filename = request.files["file"].filename
file.save("/var/www/uploads/" + filename)
开发人员认为:
用户上传一个文件,我保存下来就行。
但安全问题在于:
用户控制了文件内容和文件名。
十二、文件上传需要验证什么?
至少需要考虑:
扩展名
MIME类型
文件头
文件大小
文件内容
随机文件名
保存目录权限
是否允许执行
例如:
ALLOWED_EXTENSIONS = {
".jpg",
".jpeg",
".png",
".gif"
}
检查:
from pathlib import Path
filename = Path(upload.filename)
if filename.suffix.lower() not in ALLOWED_EXTENSIONS:
raise ValueError("unsupported file type")
但仅检查扩展名依然不够。
因为:
文件名可信
≠
文件内容可信
所以企业环境一般还会增加:
图片重新编码
病毒扫描
内容检测
对象存储
独立域名
禁止脚本执行
十三、为什么上传目录最好不要直接执行脚本?
假设 Web 服务器目录:
/var/www/html/
如果用户上传文件后直接进入:
/var/www/html/uploads/
而服务器又允许该目录执行脚本,那么一旦上传校验出现漏洞,就可能从:
上传功能
进一步发展到:
服务器端代码执行
因此一个非常重要的安全原则是:
用户上传目录与服务器脚本执行目录应该进行隔离。
例如:
应用目录
├── app/
├── config/
├── static/
└── uploads/
其中:
uploads/
只允许作为静态文件存储目录。
十四、命令执行漏洞:从参数到操作系统
再来看一个非常危险的漏洞类型。
假设开发人员写了:
import os
ip = request.args.get("ip")
os.system("ping -c 1 " + ip)
程序设计者想实现:
用户输入 IP
↓
服务器执行 ping
但是这里的问题非常严重:
用户输入
↓
Shell命令
↓
操作系统
也就是说:
应用程序把用户数据直接交给了命令解释器。
十五、更安全的做法是什么?
如果业务只是执行 ping,就不应该让用户控制完整 Shell 字符串。
例如使用参数数组:
import subprocess
ip = request.args.get("ip")
result = subprocess.run(
["ping", "-c", "1", ip],
capture_output=True,
text=True
)
print(result.stdout)
然后进一步增加:
IP格式验证
长度限制
协议限制
命令白名单
超时
权限隔离
例如:
import ipaddress
try:
ipaddress.ip_address(ip)
except ValueError:
raise ValueError("Invalid IP address")
这类思路在安全开发中非常重要。
十六、从漏洞挖掘角度如何分析一个 Web 接口?
可以建立一个固定流程。
第一步:寻找输入点
重点观察:
GET参数
POST参数
JSON字段
Header
Cookie
URL路径
文件上传
WebSocket消息
GraphQL参数
第二步:寻找敏感操作
例如:
数据库查询
文件读写
模板渲染
系统命令
反序列化
身份认证
权限检查
支付操作
后台管理
第三步:建立数据流
例如:
request.args["id"]
↓
parse()
↓
database.query()
或者:
request.form["name"]
↓
template.render()
↓
HTML
↓
Browser
第四步:寻找安全边界
安全边界包括:
认证
授权
输入校验
输出编码
权限判断
类型转换
参数化查询
安全策略
十七、使用 Python 编写一个简单的 URL 参数检查工具
下面编写一个非常基础的安全检测程序。
它不会对目标进行攻击,而是帮助安全人员快速发现 URL 中可能存在的高风险参数。
from urllib.parse import urlparse, parse_qs
SUSPICIOUS_PARAMS = {
"id",
"uid",
"user",
"file",
"path",
"url",
"redirect",
"cmd",
"query",
"search"
}
def analyze_url(url: str):
parsed = urlparse(url)
params = parse_qs(parsed.query)
print(f"URL: {url}")
print(f"Path: {parsed.path}")
if not params:
print("没有发现 URL 参数")
return
print("\\n参数分析:")
for key, value in params.items():
if key.lower() in SUSPICIOUS_PARAMS:
print(f"[重点关注] {key} = {value}")
else:
print(f"[普通参数] {key} = {value}")
if __name__ == "__main__":
test_url = (
"https://example.com/search"
"?keyword=hello&id=1001&redirect=/home"
)
analyze_url(test_url)
运行以后,可以看到类似:
URL: https://example.com/search?keyword=hello&id=1001&redirect=/home
Path: /search
参数分析:
[普通参数] keyword = ['hello']
[重点关注] id = ['1001']
[重点关注] redirect = ['/home']
这个程序虽然非常简单,但它体现了一个重要思想:
自动化工具不是替代人工分析,而是帮助我们更快找到值得人工深入分析的位置。
十八、进一步:如何建立自己的漏洞测试清单?
建议每次测试一个 Web 应用时,都按照固定清单执行。
信息收集
[ ] 域名
[ ] 子域名
[ ] IP
[ ] CDN
[ ] Web服务器
[ ] 技术栈
[ ] 开放端口
Web 接口
[ ] 登录
[ ] 注册
[ ] 搜索
[ ] 用户信息
[ ] 文件上传
[ ] 文件下载
[ ] 修改资料
[ ] 密码修改
[ ] 管理员接口
常见漏洞
[ ] SQL注入
[ ] XSS
[ ] CSRF
[ ] SSRF
[ ] 文件上传
[ ] 文件读取
[ ] 命令执行
[ ] 越权
[ ] 反序列化
[ ] 路径遍历
认证与授权
[ ] 弱密码
[ ] 登录逻辑
[ ] Session
[ ] JWT
[ ] 权限控制
[ ] 水平越权
[ ] 垂直越权
十九、为什么“越权漏洞”特别值得关注?
很多企业安全事故并不是通过复杂漏洞实现的。
而是:
一个普通用户看到了本来不应该看到的数据。
例如:
GET /api/order/10001
普通用户访问:
10001
可以查看自己的订单。
然后程序仅仅通过:
order_id
查询数据库,却没有检查:
order_id 是否属于当前用户
这就是典型的对象级授权问题。
安全的设计应该类似:
order = get_order(order_id)
if order.user_id != current_user.id:
return "Forbidden", 403
也就是说:
身份认证解决“你是谁”。
权限控制解决“你能做什么”。
两者完全不是一回事。
二十、现代 Web 安全为什么越来越强调 API?
过去 Web 应用主要依靠:
HTML
Form
Cookie
Session
现在则大量采用:
REST API
GraphQL
JWT
OAuth
WebSocket
Microservice
例如:
POST /api/v1/user/update
Content-Type: application/json
{
"username": "test",
"email": "test@example.com"
}
这意味着安全测试人员不能只看网页。
还需要重点关注:
API路径
请求方法
JSON参数
JWT
请求头
权限模型
对象ID
内部接口
二十一、API 安全测试中的几个重点
首先是:
参数越权
例如:
{
"user_id": 1002
}
如果登录用户属于:
user_id = 1001
却可以读取:
1002
就需要重点检查授权逻辑。
第二:敏感信息返回
例如接口返回:
{
"id": 1001,
"username": "test",
"email": "test@example.com",
"phone": "13800000000",
"password_hash": "xxxx",
"admin": true
}
即使密码是 Hash,也不应该无必要返回给前端。
因此 API 安全不仅要关注:
能不能访问
还要关注:
访问之后返回了什么
二十二、Burp Suite 在 Web 安全学习中的作用
如果要系统学习 Web 安全,Burp Suite 基本属于绕不开的工具。
它最核心的能力不是“自动扫描”。
而是:
拦截、修改、重放和分析 HTTP 请求。
例如:
浏览器
↓
Burp Suite
↓
服务器
一个普通请求:
GET /user?id=1001 HTTP/1.1
Host: example.com
Cookie: session=xxxx
可以被修改成:
GET /user?id=1002 HTTP/1.1
Host: example.com
Cookie: session=xxxx
然后观察:
状态码
响应长度
响应内容
响应时间
权限变化
这对于分析越权、输入校验、业务逻辑等问题非常有帮助。
二十三、自动化很重要,但不要过度依赖扫描器
很多新手一开始喜欢:
打开扫描器
↓
输入域名
↓
等待结果
这其实不是一个好的学习方式。
因为扫描器擅长的是:
快速发现
快速验证
批量检测
但不擅长理解复杂业务逻辑。
例如:
优惠券
订单
积分
支付
权限
审批
退款
这些业务往往需要人工理解业务流程。
所以更合理的工作模式是:
人工理解
↓
工具辅助
↓
自动化验证
↓
人工确认
↓
形成报告
二十四、漏洞挖掘真正应该培养的能力
学习 Web 安全的最终目标,并不是:
记住100个漏洞名称
而是建立以下几个能力。
1. 看懂请求
看到:
POST /api/user/update
能够快速判断:
身份
参数
数据类型
权限
业务目的
2. 看懂代码
看到:
sql = "SELECT * FROM user WHERE id=" + user_id
能够第一时间想到:
输入
↓
字符串拼接
↓
数据库
↓
SQL注入风险
3. 看懂业务
看到:
修改账户
能够继续思考:
修改的是自己的账户吗?
是否存在权限校验?
是否需要重新验证身份?
是否有二次确认?
是否记录审计日志?
4. 能够自动化
例如使用 Python:
import requests
url = "https://example.com/api/user"
response = requests.get(
url,
timeout=10
)
print("Status:", response.status_code)
print("Length:", len(response.text))
再进一步可以构建:
资产收集
↓
接口整理
↓
参数分析
↓
风险分类
↓
结果输出
最终形成属于自己的安全工具链。
二十五、一个适合新手的 Web 安全学习路线
如果你刚开始学习网络安全,可以按照下面的顺序进行。
第一阶段:网络基础
学习:
TCP/IP
HTTP
DNS
TLS
Cookie
Session
代理
端口
Socket
第二阶段:Linux
掌握:
cd
ls
cat
grep
find
awk
sed
curl
wget
ps
top
netstat
ss
chmod
chown
重点不是背命令,而是理解:
Linux 系统到底是怎么工作的。
第三阶段:编程
推荐至少掌握:
Python
JavaScript
SQL
Shell
其中 Python 最适合安全自动化。
第四阶段:Web 开发基础
至少能够看懂:
HTML
CSS
JavaScript
HTTP
Flask
Django
Node.js
SQL
因为:
不懂开发,就很难深入理解漏洞。
第五阶段:漏洞原理
建议按照:
SQL注入
XSS
CSRF
文件上传
路径遍历
SSRF
命令执行
反序列化
越权
JWT安全
逐步学习。
二十六、建议搭建自己的安全实验环境
学习漏洞最好的方式之一,就是:
在自己搭建的实验环境中进行验证。
例如:
Windows
↓
VMware / VirtualBox
↓
Linux
↓
Docker
↓
靶场
可以选择搭建:
DVWA
WebGoat
Juice Shop
bWAPP
这些环境非常适合学习常见 Web 安全问题。
二十七、Docker 搭建测试环境的基本思路
例如:
docker pull bkimminich/juice-shop
然后:
docker run -d \\
–name juice-shop \\
-p 3000:3000 \\
bkimminich/juice-shop
查看容器:
docker ps
查看日志:
docker logs juice-shop
进入实验环境:
http://127.0.0.1:3000
这样就可以在完全隔离、自己控制的环境中进行学习。
二十八、从“漏洞思维”升级到“攻击面思维”
到了进阶阶段,不要再只盯着某一种漏洞。
应该开始思考:
一个系统有多少入口?
例如一个企业平台可能存在:
Web站点
移动端API
管理后台
文件服务器
第三方登录
消息系统
内部API
WebSocket
对象存储
这些都属于攻击面的一部分。
因此:
真正高级的安全思维,不是“我会什么漏洞”,而是“这个系统哪里可能出问题”。
二十九、企业安全开发中的几个关键原则
从防御角度看,很多安全问题其实都可以提前规避。
原则一:永远不要信任用户输入
GET参数
POST参数
Cookie
Header
JSON
文件
理论上都应该被视为:
不可信数据
原则二:最小权限
数据库账户:
不要给 root
应用进程:
不要使用 root
文件权限:
只开放必要权限
原则三:默认拒绝
权限系统建议:
没有权限
↓
拒绝
而不是:
没有明确禁止
↓
允许
原则四:日志审计
至少记录:
登录
退出
权限变化
敏感操作
异常请求
管理员操作
数据导出
这样出现安全事件之后,才能进行追溯。
三十、总结:真正的 Web 安全,是理解系统,而不是背漏洞
当我们把今天讲到的内容放在一起,会发现所有漏洞都存在一个共同规律:
用户输入
↓
应用处理
↓
危险操作
↓
安全边界失效
↓
产生安全问题
SQL 注入:
用户输入 → SQL
XSS:
用户输入 → HTML / JavaScript
命令执行:
用户输入 → Shell
文件上传:
用户文件 → Web目录
越权:
用户身份 → 未正确检查资源权限
CSRF:
用户身份 → 非预期业务请求
因此,学习 Web 安全最重要的不是背 Payload,而是建立一种稳定的分析框架:
第一步:找到输入点
第二步:追踪数据流
第三步:寻找危险操作
第四步:检查安全边界
第五步:验证漏洞影响
第六步:提出修复方案
当你能够看到一个接口,就自然想到:
输入在哪里?
权限在哪里?
数据去了哪里?
谁可以调用?
服务器怎么处理?
这时,你才算真正开始进入 Web 安全的世界。
三十一、写给正在学习网络安全的同学
网络安全学习最容易出现的问题,就是:
今天学SQL注入
明天学XSS
后天学Linux
大后天学Kali
然后开始迷茫
真正合理的方法应该是:
网络基础
↓
Linux
↓
编程
↓
Web原理
↓
漏洞原理
↓
靶场实践
↓
源码审计
↓
自动化
↓
真实项目安全
不要一开始就追求“高级”。
先把最基础的 HTTP、Linux、Python、SQL、JavaScript 学明白,再逐步深入。
因为:
所有看起来复杂的安全漏洞,最终都可以拆解成一些非常基础的技术问题。
当你真正理解这些基础之后,很多漏洞就不再神秘。
结语
Web 安全是一个需要长期积累的方向。
漏洞会变,框架会变,技术栈会变,攻击方式也会不断变化。
但底层逻辑不会轻易改变:
理解协议
理解系统
理解代码
理解业务
理解数据流
理解权限
这也是网络安全工程师真正的核心能力。
对于初学者来说,与其每天寻找“最新漏洞利用”,不如先花时间把:
HTTP
Linux
Python
SQL
JavaScript
Web架构
这些基础打牢。
当基础足够扎实之后,你会发现:
漏洞不是需要死记硬背的知识,而是系统设计出现问题之后自然产生的结果。
文章标签








