📚 网络安全 Kali 学习系列
模块 0 基础阶段 ✅ 已完成
01-18 Linux系统 → 计算机网络 → 密码学 → Python/Bash/PHP/JS → Docker
模块 1 信息收集与侦察 ✅ 已完成
19 nmap 主动扫描 → 20 masscan 高速扫描 → 21 指纹识别与目录爆破 → 22 漏洞扫描与 Web 安全基础
模块 2 Web 应用渗透(攻击技术阶段)
✓ 23 sqlmap 注入与 Burp 抓包
✓ 24 XSS 跨站脚本攻击
✓ 25 CSRF 与文件上传漏洞
✓ 26 命令执行与代码执行漏洞
✓ 27 文件包含与 SSRF 漏洞
✓ 28 反序列化漏洞
✓ 29 XXE 外部实体注入
30 越权访问与逻辑漏洞(当前篇)
31 WAF 绕过技术(预告)
模块 3-5 后续持续更新
导读
前 7 篇我们学的 SQL 注入、XSS、CSRF、文件上传、命令执行、反序列化、XXE,每一个都需要找到代码中的技术缺陷。但有一类漏洞完全不同——它不需要注入恶意 Payload,不需要绕过过滤器,不需要利用解析器特性,只需改一个数字:把 URL 中的 ?uid=1001 改成 ?uid=1002,就能看到别人的订单、邮件、工资单。这就是越权访问(IDOR)——OWASP Top 10 2025 中 A01 Broken Access Control 的核心成员,94% 的测试应用中存在此问题。
读完本篇你将能:区分水平越权与垂直越权的攻击模型、手工构造 IDOR Payload 枚举其他用户数据、识别支付篡改/验证码绕过/密码找回等逻辑漏洞模式、使用 Burp Suite Autorize 插件自动化检测越权、为应用设计服务端鉴权与零信任防御体系。
目录
1. 访问控制基础:认证 vs 授权
2. 越权访问:水平越权与垂直越权
3. IDOR 漏洞实战:从改 ID 到数据外带
4. 逻辑漏洞:不靠技术缺陷的攻击
5. 自动化越权检测:Burp Autorize + AutorizePro
6. 防御方案:服务端鉴权、UUID 与零信任
7. 伦理边界与分级练习
1. 访问控制基础:认证 vs 授权
大多数越权漏洞的根源是开发者混淆了「认证」和「授权」两个概念。认证回答「你是谁」,授权回答「你能做什么」。一个用户登录成功(认证通过),不代表他可以访问所有数据(授权检查缺失)。
1.1 认证与授权的区别
认证(Authentication):验证用户身份。用户输入账号密码,服务端验证无误后创建会话(Session/JWT)。此时服务端知道「这是用户 A」,但还没有检查「用户 A 能否访问订单 #1234」
授权(Authorization):验证用户权限。用户 A 请求查看订单 #1234,服务端必须检查:这个订单属于用户 A 吗?如果缺少这一步,用户 A 只需把 URL 中的订单号改成 #1235,就能看到用户 B 的订单——这就是 IDOR
用一个生活类比:认证相当于刷工牌进入公司大门,授权相当于刷卡打开你自己的办公室。如果进了大门后所有办公室都不锁门,任何人都能进入任何办公室——这就是越权漏洞的本质。
1.2 访问控制的三种模式
| 模式 |
原理 |
典型漏洞 |
| DAC |
自主访问控制:资源所有者决定谁能访问 |
所有者配置错误导致权限扩散 |
| MAC |
强制访问控制:系统按安全等级统一管控 |
配置复杂,实际应用少见 |
| RBAC |
基于角色的访问控制:用户→角色→权限 |
角色层级设计不当导致垂直越权 |
Web 应用最常用的是 RBAC。用户被分配角色(如「普通用户」「管理员」),角色绑定权限(如「读自己的订单」「读所有订单」)。RBAC 的安全基础在于:每次访问对象时,服务端必须检查当前用户角色是否有权操作该特定对象。如果只检查角色但不检查对象归属,就会产生越权。
1.3 OWASP 中的定位
在 OWASP Top 10 2025 中, Broken Access Control 排名 A01——第一位。OWASP 研究显示,94% 的被测应用存在某种形式的访问控制缺陷。这个类别合并了多个子漏洞:IDOR(不安全的直接对象引用)、水平越权、垂直越权、功能级访问控制缺失、路径遍历。前 7 篇文章讲的注入类漏洞需要找到代码中的技术缺陷,而访问控制漏洞只需要一个疏忽——忘记在某个接口加权限检查。
2. 越权访问:水平越权与垂直越权
越权访问分为两个方向:水平越权(同级用户之间互看数据)和垂直越权(低权限用户执行高权限操作)。两者的攻击路径不同,防御重点也不同。
2.1 水平越权
水平越权是同级别用户之间的越权。用户 A 和用户 B 都是普通用户,但 A 通过修改请求参数(如用户 ID、订单号)访问到了 B 的数据。
水平越权示例
# 用户 A 正常请求自己的订单
GET /api/orders?uid=1001 HTTP/1.1
Cookie: session=aaa111
# 用户 A 修改 uid 查看用户 B 的订单
GET /api/orders?uid=1002 HTTP/1.1
Cookie: session=aaa111
# 服务端没有校验 uid 与 session 的对应关系
# 直接返回了用户 B 的订单数据
HTTP/1.1 200 OK
{"order_id": 5678, "user": "B", "amount": 999}
这种漏洞的代码模式通常是这样的:服务端从请求参数中获取 uid,查询数据库返回该用户的订单,但从未检查这个 uid 是否与当前登录用户的 Session 一致。
PHP 漏洞代码
// 漏洞:信任了用户传入的 uid
$uid = $_GET['uid']; // 直接取用户传入的 uid
$orders = db_query("SELECT * FROM orders WHERE user_id = ?", $uid);
json_encode($orders); // 返回任意用户的订单
// 修复:从 Session 中获取 uid,不信任用户传入
$uid = $_SESSION['user_id']; // 从服务端 Session 取
$orders = db_query("SELECT * FROM orders WHERE user_id = ?", $uid);
2.2 垂直越权
垂直越权是低权限用户冒充高权限用户执行操作。普通用户通过修改请求路径或参数,访问到管理员才能使用的功能。
垂直越权示例
# 普通用户正常请求
GET /user/profile HTTP/1.1
Cookie: session=user_token
# 直接访问管理后台接口
GET /admin/users/list HTTP/1.1
Cookie: session=user_token
# 服务端只检查了是否登录,没有检查角色
# 普通用户成功获取了所有用户列表
HTTP/1.1 200 OK
[{"id":1,"name":"admin","role":"superadmin"},...]
垂直越权常见于管理后台。开发者常犯的错误:在前端隐藏管理入口(如不渲染管理菜单链接),但后端没有对 /admin/* 路径做角色检查。攻击者不需要看到菜单——只要知道 URL 就能直接访问。
Java 漏洞代码
// 漏洞:只检查登录状态,不检查角色
@GetMapping("/admin/users")
@PreAuthorize("isAuthenticated()") // 只检查登录
public List<User> listUsers() { ... }
// 修复:检查角色权限
@GetMapping("/admin/users")
@PreAuthorize("hasRole('ADMIN')") // 检查角色
public List<User> listUsers() { ... }
💡 小贴士
垂直越权最容易出现的场景是 API 版本迭代。V1 版本中 /api/v1/admin 有完整的角色检查,V2 版本重构时新写了 /api/v2/admin,但 V2 的鉴权中间件忘了对 admin 路径做角色校验——只检查了 JWT 是否有效。旧版本下线后,V2 的越权漏洞就成了唯一入口。审计时重点关注新旧 API 版本的鉴权一致性。
3. IDOR 漏洞实战:从改 ID 到数据外带
IDOR(Insecure Direct Object Reference)是水平越权最典型的形式。攻击者直接在请求中引用对象 ID(用户 ID、订单号、文件 ID),服务端不做归属校验就返回数据。本节从实战角度拆解 IDOR 的完整利用流程。
3.1 IDOR 的常见位置
IDOR 不只出现在 URL 参数中。攻击者需要全面检查所有可能携带对象引用的位置:
| 位置 |
示例 |
隐蔽性 |
| URL 路径 |
/api/orders/1234 |
低(最容易被发现) |
| Query 参数 |
?uid=1001&order=5678 |
低 |
| POST Body |
{"user_id": 1001, "action": "view"} |
中 |
| Cookie |
Set-Cookie: last_viewed=5678 |
高(容易被忽略) |
| JWT Payload |
{"sub": 1001, "role": "user"} |
高(需解码 JWT) |
| GraphQL 变量 |
query { user(id: 1001) { email } } |
高(GraphQL 端点隐蔽) |
3.2 IDOR 枚举:从 1 到 N
找到 IDOR 入口后,攻击者可以遍历 ID 枚举所有用户的数据。如果 ID 是连续整数(1, 2, 3...),枚举极为简单。用 Burp Suite Intruder 模块批量替换 ID:
Burp Intruder IDOR 枚举
# 原始请求
GET /api/orders/§1001§ HTTP/1.1
Cookie: session=aaa111
# Payload: 从 1000 到 2000 的整数
# Intruder 自动遍历 1001 个订单 ID
# 根据 Content-Length 排序找到有数据的响应
# 或用 ffuf 快速模糊测试
ffuf -u http://target.com/api/orders/FUZZ \
-H "Cookie: session=aaa111" \
-w <(seq 1000 2000) \
-mc 200 -fs 0
3.3 IDOR 的进阶利用
IDOR 不只是读取数据——如果越权涉及写入操作(修改、删除),危害更大。以下是一些进阶场景:
越权修改:PUT /api/users/1002/password → 修改别人的密码
越权删除:DELETE /api/orders/5678 → 删除别人的订单
越权操作:POST /api/transfer?from=1001&to=1002&amount=9999 → 从别人账户转账
批量数据外带:遍历所有用户 ID,用脚本批量拉取并整理成 CSV
UUID 猜测:即使使用 UUID,如果 UUID 在其他接口(如用户头像 URL、分享链接)中泄露,仍可被利用
💡 小贴士
很多人以为把连续 ID 换成 UUID 就万事大吉了。但 UUID 的安全性依赖于「不可猜测」——如果 UUID 在任何地方泄露过(列表接口返回了其他用户的 UUID、日志文件中记录了 UUID、Referer 头中携带了 UUID),攻击者就能拿到它。UUID 是纵深防御的一层,但不能替代服务端鉴权。真正的修复只有一个:每次访问对象时,服务端验证当前用户是否有权操作该对象。
4. 逻辑漏洞:不靠技术缺陷的攻击
逻辑漏洞是安全测试中最难自动化检测的漏洞类型——因为它不依赖代码中的技术缺陷(如 SQL 注入、XSS),而是利用业务流程设计中的疏忽。扫描器无法发现逻辑漏洞,因为扫描器不理解业务规则。逻辑漏洞只能通过人工理解业务流程后构造攻击场景来发现。
4.1 支付篡改
电商支付流程中,客户端发送的金额如果被服务端直接信任,攻击者可以把 999 元的商品改成 1 分钱:
支付篡改漏洞
# 正常下单
POST /api/order/create HTTP/1.1
{"product_id": 88, "quantity": 1, "price": 999.00}
# 攻击者篡改 price
POST /api/order/create HTTP/1.1
{"product_id": 88, "quantity": 1, "price": 0.01}
# 服务端直接信任了客户端传入的 price
# 订单创建成功,支付 1 分钱购买 999 元商品
修复方案:服务端永远不信任客户端传入的价格。商品价格应从服务端数据库中查询,不能从请求参数中获取:
Python 修复代码
def create_order(request):
product_id = request.json['product_id']
quantity = request.json['quantity']
# 从数据库查价格,不信任客户端
product = db.get_product(product_id)
total = product.price * quantity
order = Order(user_id=session['uid'],
product_id=product_id,
amount=total)
db.save(order)
4.2 验证码绕过
验证码绕过的逻辑漏洞有多种形式,核心都是服务端验证逻辑设计不当:
验证码永久有效:服务端生成验证码后存入 Session 但不设过期时间。攻击者获取一次验证码后可以无限次重用
验证码不过期:设置了过期时间但过期后不清理。攻击者等待过期后提交旧验证码仍然通过
空验证码绕过:服务端代码逻辑缺陷——当验证码参数为空或不存在时,跳过了验证步骤
客户端验证:验证码校验在前端 JS 中完成。攻击者直接发送不带验证码参数的请求,绕过前端
万能验证码:开发阶段留下的后门验证码(如 0000、1234),上线后忘记删除
空验证码绕过代码
// 漏洞:当 captcha 参数不存在时跳过校验
if (captcha) { // 如果 captcha 不为空才验证
if (captcha !== session.captcha) {
return res.json({error: '验证码错误'});
}
}
// captcha 参数不存在时直接通过!
// 修复:验证码必须存在且正确
if (!captcha || captcha !== session.captcha) {
return res.json({error: '验证码错误或已过期'});
}
4.3 密码找回逻辑缺陷
密码找回是逻辑漏洞的重灾区——因为找回流程涉及多个步骤(发送验证码、验证验证码、重置密码),任何一步的校验缺失都可能导致任意用户密码重置。
步骤跳过:找回密码有 3 步(1.发送验证码 → 2.验证验证码 → 3.重置密码)。如果攻击者直接访问第 3 步的 URL 且服务端不检查是否完成第 2 步,就能跳过验证码直接重置密码
验证码可爆破:4 位数字验证码有 10000 种组合,如果没有限制尝试次数,攻击者可以爆破
用户身份篡改:第 1 步为用户 A 发送验证码,第 3 步重置时把用户标识改为 B,实现了用 A 的验证码重置 B 的密码
Token 不过期:密码重置 Token 无过期时间,攻击者可以长期使用泄露的 Token
密码找回步骤跳过
# 步骤1: 为用户 A 发送验证码
POST /api/reset/send?email=victim@mail.com
# 步骤2: 跳过验证码验证
# 直接请求步骤3的重置接口
POST /api/reset/confirm
{"email": "victim@mail.com", "new_password": "hacked123"}
# 服务端没有检查步骤2是否完成
# 直接重置了 victim 的密码
常见错误
开发者最常犯的逻辑错误是「前端控制流程」——用 JavaScript 控制步骤跳转(step 1 完成后用 window.location 跳到 step 2),但后端不维护步骤状态。攻击者直接请求最后一步的 API 就能跳过中间所有验证。正确做法:服务端用 Session 记录当前步骤,每个步骤的 API 都检查前序步骤是否完成。
4.4 其他常见逻辑漏洞
并发条件竞争:优惠券领取接口没有加锁,攻击者用并发请求同时领取同一优惠券 100 次,实际只能领 1 次的限制被绕过
负数数量:下单时 quantity 传 -1,总价变成负数,系统反而退款给攻击者
整数溢出:quantity 传 2147483648(2^31),32 位整数溢出后变成负数或 0
重复使用优惠:优惠券使用后状态没有及时更新,攻击者快速连续请求可以重复使用同一张优惠券
越权修改个人信息:修改自己资料的接口同时允许修改角色字段——攻击者把 role 从 user 改成 admin
5. 自动化越权检测:Burp Autorize + AutorizePro
逻辑漏洞和 IDOR 很难用传统扫描器发现——因为它们需要理解业务逻辑。但越权漏洞有一个特点:可以通过「双账号对比」自动化检测。核心思路是:用低权限账号的 Cookie 替换高权限账号的请求,看服务端是否仍然返回高权限数据。
5.1 Burp Suite Autorize 插件
Autorize 是 Burp Suite 的扩展插件,专门用于自动化检测越权漏洞。它的工作原理:拦截浏览器发出的每个请求,自动用预设的低权限 Cookie 重放请求,对比两次响应的差异。如果低权限 Cookie 也获得了与高权限 Cookie 相同的数据,就标记为越权漏洞。
使用流程:
Autorize 使用流程
# 1. 用高权限账号(A)登录浏览器
# 2. 在 Autorize 配置中填入低权限账号(B)的 Cookie
Configuration → Cookie: session=bbb222
# 3. 正常浏览网站(用账号 A 操作)
# Autorize 自动用账号 B 的 Cookie 重放每个请求
# 4. 检查 Autorize 面板中的结果
# 红色 = Bypass(越权!A 和 B 获得相同数据)
# 黄色 = Is Enforced(B 被正确拒绝)
# 灰色 = Skipped(URL 不在测试范围内)
5.2 AutorizePro:AI 辅助越权检测
AutorizePro 是 Autorize 的增强版本,内置 AI 分析模块。它不仅对比 Cookie 替换前后的响应差异,还用 AI 分析响应内容中是否包含真正的敏感信息泄露——减少误报(如两个账号返回相同框架页面但不含敏感数据的情况)。
💡 小贴士
越权检测的常见误报场景:两个用户访问同一公共页面(如首页),响应内容相同是正常的。Autorize 通过响应相似度判断是否越权,首页会标记为红色(相同响应),但实际不是漏洞。解决方法:在 Autorize 的 Filter 中配置 URL 过滤规则,排除公共页面路径(如 /、/login、/about),只测试包含用户数据的 API 路径。
5.3 手工检测流程
即使有自动化工具,手工检测仍是不可替代的——特别是逻辑漏洞,无法自动化发现。标准流程:
手工越权检测流程
# 1. 注册两个账号:A(高权限)和 B(低权限)
# 账号 A: admin@test.com (管理员)
# 账号 B: user@test.com (普通用户)
# 2. 用账号 A 正常操作,在 Burp Proxy 中抓包
# 3. 找到包含对象 ID 的请求
GET /api/orders/1001 → 属于账号 A
# 4. 发到 Repeater,替换 Cookie 为账号 B
Cookie: session=bbb222 → 替换为账号 B 的会话
# 5. 发送请求,观察响应
# 200 + 数据 → 水平越权
# 403/401 → 权限正常
# 6. 垂直越权测试:用账号 B 直接访问 /admin/* 路径
GET /admin/users/list
Cookie: session=bbb222 → 普通用户 Cookie
6. 防御方案:服务端鉴权、UUID 与零信任
越权漏洞的防御核心原则:服务端必须对每次对象访问进行授权检查,不信任任何客户端传入的身份标识。以下从代码、架构、流程三个层面给出防御方案。
6.1 代码层面:服务端强制鉴权
Python 安全模式
# 从 Session 获取当前用户,不信任请求参数
def get_order(request, order_id):
uid = request.session['user_id']
# 查询时附加用户条件,确保只能查到自己的订单
order = Order.query.filter_by(
id=order_id,
user_id=uid # 关键:附加 user_id 条件
).first()
if not order:
return jsonify({'error': '订单不存在或无权访问'}), 404
return jsonify(order.to_dict())
6.2 架构层面:纵深防御
| 层级 |
措施 |
防御目标 |
| L1 |
使用 UUID v4 替代连续 ID |
增加枚举难度 |
| L2 |
服务端 Session 获取用户身份 |
杜绝客户端伪造 uid |
| L3 |
数据库查询附加 user_id 条件 |
数据层兜底 |
| L4 |
RBAC 中间件检查角色权限 |
阻断垂直越权 |
| L5 |
API 速率限制(Rate Limit) |
阻断枚举攻击 |
6.3 逻辑漏洞防御清单
价格:服务端从数据库查询商品价格,不信任客户端传入
验证码:必须存在 + 必须正确 + 必须有过期时间 + 必须限制尝试次数
步骤:服务端用 Session 维护当前步骤,每个步骤 API 检查前序步骤
并发:关键操作(领取、扣款)加分布式锁或数据库乐观锁
数量:校验 quantity 为正整数,防止负数和溢出
字段:用户修改接口用 DTO 白名单过滤可修改字段,禁止修改 role 等敏感字段
Java DTO 白名单示例
// 用 DTO 限制用户可修改的字段
public class UserUpdateDTO {
private String nickname; // 允许修改
private String avatar; // 允许修改
// 没有 role 字段 → 即使请求中传了 role 也会被忽略
}
@PostMapping("/user/update")
public Result update(@RequestBody UserUpdateDTO dto) { ... }
7. 伦理边界与分级练习
7.1 法律红线
越权漏洞的利用往往比注入类漏洞更危险——因为越权直接访问真实用户数据。即使是在测试环境中,枚举其他用户的数据也涉及隐私问题。根据《个人信息保护法》和《刑法》第二百五十三条,非法获取公民个人信息可处三年以下有期徒刑并处罚金。所有练习必须在授权靶场中进行,禁止在真实系统中测试。
合法练习靶场:
PortSwigger Web Security Academy — Access Control 实验室系列(含 IDOR、垂直越权、多步骤逻辑)
DVWA — Access Control 模块
crAPI(Completely Ridiculous API)— 专为 API 安全设计的靶场,含多种越权场景
自建靶场 — 用 Docker 部署包含越权缺陷的 PHP/Java/Python 应用
7.2 分级练习
入门级
1. 注册两个账号,用 Burp Repeater 手工测试水平越权(替换 Cookie 访问对方订单)
2. 在 PortSwigger 完成「IDOR basic exercise」和「Broken Access Control」入门实验室
3. 构造支付篡改 Payload:修改 POST Body 中的 price 字段
4. 测试空验证码绕过:删除请求中的 captcha 参数观察是否通过
5. 修复漏洞代码:把 $_GET['uid'] 改为 $_SESSION['user_id']
进阶级
1. 安装 Burp Autorize 插件,用双账号自动化检测越权
2. 在 PortSwigger 完成「Multi-step processes」逻辑漏洞实验室
3. 测试密码找回步骤跳过漏洞
4. 构造负数数量订单 Payload,观察服务端是否校验
5. 用 ffuf 对 IDOR 接口进行 ID 枚举(配合 Rate Limit 检测)
挑战级
1. 在 crAPI 靶场完成所有越权相关挑战
2. 编写 Python 脚本实现自动化 IDOR 检测(双账号 Cookie 对比)
3. 发现 GraphQL 接口中的 IDOR(替换查询变量中的用户 ID)
4. 测试并发条件竞争漏洞(用 Python asyncio 并发请求优惠券领取接口)
5. 在自建应用中实现完整的纵深防御体系(UUID + Session 鉴权 + DB 条件 + RBAC + Rate Limit)
知识回顾
认证 vs 授权
水平越权
垂直越权
IDOR
A01 Broken Access Control
支付篡改
验证码绕过
密码找回逻辑
条件竞争
Burp Autorize
UUID
DTO 白名单
零信任
下篇预告
31 WAF 绕过技术:编码、分块与参数污染
将学习 WAF(Web Application Firewall)的绕过技术——当目标部署了 ModSecurity、Cloudflare 等 WAF 时,如何通过编码转换、分块传输、HTTP 参数污染等手段绕过规则过滤。这是模块 2 Web 应用渗透的收官篇,之后将进入模块 3 代码审计阶段。