提交 7e8ab845 authored 作者: 陈泽健's avatar 陈泽健

docs(prd): 更新接口安全测试需求文档

- 添加已知安全问题基线表格,包含14项已确认安全问题清单
- 补充Nginx安全配置分析,包括外露路径和缺失安全配置
- 增加历史漏洞回归测试清单,涵盖35项高危、中危、低危漏洞
- 完善华为安全红线检查清单,包含22项接口自动化可测试项
- 扩展OWASP API Security Top 10测试范围,增加历史漏洞回归列
- 细化各系统安全测试内容,包括预定系统、运维集控、讯飞转录等
- 添加SQL注入重点目标和注入载荷类型说明
- 补充测试执行优先级矩阵,明确P0-P3级别测试类别
- 更新验收要求,增加历史漏洞和华为安全红线验证标准
- 新增参考资料利用率统计,显示34个文件中30个已利用,覆盖率达94.1%
- 添加优化功能回填记录,追踪文档完善进度
- 扩展知识库数据,增加长安深蓝汽车POC漏洞数据映射
上级 495ecfeb
......@@ -790,6 +790,188 @@ def test_2_2_11(client, auth):
)
def test_2_2_12(client, auth):
"""
2.2.12: NoSQL注入绕过登录认证 (HV-026, CVSS 9.4 紧急)
来源: HCL AppScan 2025-12-18 安全报告
测试接口: POST /platform/api/auth/login
在username字段注入MongoDB $ne操作符尝试绕过认证
"""
test_id = "2.2.12"
name = "NoSQL注入绕过登录认证"
log.info(f"[{test_id}] {name}")
login_path = client.config['auth'].get('login_path', '/platform/api/auth/login')
captcha = client.config['accounts'].get('captcha', 'csba')
# NoSQL注入载荷 — 使用 $ne 操作符绕过用户名校验
nosql_payloads = [
# $ne: 不等于,匹配第一个非"1"的用户
{"username": {"$ne": "1"}, "password": "anypassword", "code": captcha, "uuid": ""},
# $gt: 大于,匹配所有用户
{"username": {"$gt": ""}, "password": "anypassword", "code": captcha, "uuid": ""},
# $regex: 正则匹配
{"username": {"$regex": ".*"}, "password": "anypassword", "code": captcha, "uuid": ""},
# $nin: 不在列表中
{"username": {"$nin": ["nonexistent"]}, "password": "anypassword", "code": captcha, "uuid": ""},
]
vulnerable_payloads = []
for i, payload in enumerate(nosql_payloads):
uuid_val = client.get_captcha_uuid() or ''
payload["uuid"] = uuid_val
sign_headers = client._generate_sign(body_data=payload, bearer_token="")
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json, text/plain, */*',
'referer': '',
}
headers.update(sign_headers)
try:
url = f"{client.base_url}{login_path}"
resp = client.session.post(
url, json=payload, headers=headers,
verify=client.verify_ssl, timeout=client.timeout
)
if resp.status_code == 200:
try:
data = resp.json()
# 检查是否登录成功(获取到Token)
token = (data.get('accessToken') or
data.get('token') or
(data.get('data', {}) or {}).get('accessToken') or
(data.get('data', {}) or {}).get('token'))
if token:
vulnerable_payloads.append(f"载荷{i+1}: {list(payload['username'].keys())}")
log.warning(f"[{test_id}] NoSQL注入成功!载荷: {payload['username']}")
except Exception:
pass
except Exception as e:
log.debug(f"[{test_id}] NoSQL注入测试请求{i+1}异常: {e}")
request_info = _build_request_info(
"POST", login_path,
json_data={"username": {"$ne": "1"}, "password": "...", "code": captcha}
) + "\n攻击方式: MongoDB $ne/$gt/$regex/$nin 操作符注入"
if vulnerable_payloads:
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_CRITICAL,
description=f"登录接口存在NoSQL注入漏洞(CVSS 9.4),以下注入载荷成功绕过认证: "
f"{vulnerable_payloads}。"
f"攻击者无需密码即可登录任意账户,属于紧急安全漏洞",
request_info=request_info,
response_info=f"成功绕过的载荷: {vulnerable_payloads}",
is_vulnerable=True,
fix_suggestion="1. 对username参数进行严格类型校验,确保为string类型而非object;"
"2. 在反序列化层拦截非标准JSON结构;"
"3. 使用参数化查询而非直接拼接查询条件"
)
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_CRITICAL,
description="NoSQL注入攻击均被正确拦截,登录接口参数类型校验正常",
request_info=request_info,
response_info="所有NoSQL注入载荷均未成功登录",
is_vulnerable=False
)
def test_2_2_13(client, auth):
"""
2.2.13: 注销后Token/会话未失效测试 (HV-030)
来源: 南山区委前台渗透测试报告
验证注销接口调用后旧Token是否仍然有效
"""
test_id = "2.2.13"
name = "注销后Token未失效(会话管理)"
log.info(f"[{test_id}] {name}")
# 第一步:获取有效的user Token
user_token = auth.get_user_token()
if not user_token:
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_INFO,
description="user Token 获取失败,跳过测试",
is_vulnerable=False
)
# 第二步:用该Token验证可以访问认证接口
verify_resp_before = client.get("/api/system/getUserInfo",
token=user_token, account=None)
if not (verify_resp_before and verify_resp_before.status_code == 200):
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_INFO,
description="Token验证接口不可用,跳过测试",
is_vulnerable=False
)
# 第三步:调用注销接口
logout_paths = [
"/platform/api/auth/logout",
"/platform/api/system/logout",
]
logout_success = False
for logout_path in logout_paths:
logout_resp = client.post(logout_path, token=user_token)
if logout_resp and logout_resp.status_code == 200:
logout_success = True
break
if not logout_success:
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_INFO,
description=f"注销接口调用失败(尝试: {logout_paths}),跳过测试",
is_vulnerable=False
)
# 第四步:用旧Token再次尝试访问认证接口
import time
time.sleep(1) # 等待服务端处理注销
verify_resp_after = client.get("/api/system/getUserInfo",
token=user_token, account=None)
request_info = (f"测试步骤:\n"
f"1. 获取user Token并验证可访问 /api/system/getUserInfo\n"
f"2. 调用注销接口 {logout_path}\n"
f"3. 使用旧Token再次访问 /api/system/getUserInfo\n"
f"旧Token: {user_token[:50]}...")
if verify_resp_after and verify_resp_after.status_code == 200:
try:
data = verify_resp_after.json()
code = data.get('code', data.get('status'))
if code and str(code) not in ('401', '403', '40301', '40101'):
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_HIGH,
description="注销后旧Token仍然有效,可正常访问认证接口。"
"存在会话管理漏洞,攻击者获取已注销的Token后仍可持续访问",
request_info=request_info,
response_info=f"注销前: HTTP {verify_resp_before.status_code}\n"
f"注销后: HTTP {verify_resp_after.status_code}, body: {_safe_json(verify_resp_after)}",
is_vulnerable=True,
fix_suggestion="1. 注销时在服务端将Token加入黑名单或从缓存中删除;"
"2. 设置Token合理的过期时间;"
"3. 确保注销后所有关联的会话标识全部失效"
)
except Exception:
pass
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_HIGH,
description=f"注销后旧Token已被正确失效(HTTP {verify_resp_after.status_code if verify_resp_after else 'None'}),"
f"会话管理机制正常",
request_info=request_info,
response_info=f"注销后响应: {_safe_json(verify_resp_after)}",
is_vulnerable=False
)
def run_tests(client, auth):
"""
执行 API2 所有身份认证失效测试用例
......@@ -817,6 +999,9 @@ def run_tests(client, auth):
results.append(test_2_2_9(client, auth))
results.append(test_2_2_10(client, auth))
results.append(test_2_2_11(client, auth))
# 新增:基于参考资料补充的测试用例
results.append(test_2_2_12(client, auth)) # HV-026 NoSQL注入 CVSS 9.4
results.append(test_2_2_13(client, auth)) # HV-030 注销后Token未失效
log.info(f"API2 测试完成,共 {len(results)} 个用例")
return results
......@@ -626,6 +626,194 @@ def _find_sensitive_in_dict(data, keyword, parent_key="", depth=0):
return found
def test_2_3_8(client, auth):
"""
2.3.8: API成批分配漏洞测试 (HV-027, CVSS 7.3)
来源: HCL AppScan 2025-12-18 安全报告
测试接口: POST /meetingV3/api/manageUser/getManagerPageForBook
在请求体中注入is_admin/is_sso/role字段尝试特权升级
"""
test_id = "2.3.8"
name = "API成批分配-注入特权属性"
log.info(f"[{test_id}] {name}")
user_token = auth.get_user_token()
if not user_token:
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_INFO,
description="user Token 获取失败,跳过测试",
is_vulnerable=False
)
# 获取companyNumber
try:
config = client.config
company_number = config.get('company_number', 'CN-WQF-UBAINS')
except Exception:
company_number = 'CN-WQF-UBAINS'
# 正常请求 vs 注入特权属性的请求
normal_payload = {
"companyNumber": company_number,
"pageSize": 5,
"pageNum": 1,
}
# 注入特权升级字段
mass_assignment_payloads = [
{
"companyNumber": company_number, "pageSize": 5, "pageNum": 1,
"is_admin": True,
},
{
"companyNumber": company_number, "pageSize": 5, "pageNum": 1,
"is_sso": True,
},
{
"companyNumber": company_number, "pageSize": 5, "pageNum": 1,
"role": "admin",
},
{
"companyNumber": company_number, "pageSize": 5, "pageNum": 1,
"isAdmin": True, "roleId": 1,
},
]
# 先发正常请求获取基准响应
test_path = "/meetingV3/api/manageUser/getManagerPageForBook"
normal_resp = client.post(test_path, json_data=normal_payload, token=user_token)
normal_record_count = 0
if normal_resp and normal_resp.status_code == 200:
try:
data = normal_resp.json()
records = (data.get('result', {}).get('records', []) or
data.get('data', {}).get('records', []) or [])
normal_record_count = len(records)
except Exception:
pass
# 测试每个注入载荷
vulnerable_payloads = []
for i, payload in enumerate(mass_assignment_payloads):
resp = client.post(test_path, json_data=payload, token=user_token)
if resp and resp.status_code == 200:
try:
data = resp.json()
records = (data.get('result', {}).get('records', []) or
data.get('data', {}).get('records', []) or [])
# 如果注入后返回的数据量明显多于正常请求,说明特权升级成功
if len(records) > normal_record_count:
vulnerable_payloads.append(f"载荷{i+1}: {list(payload.keys())}")
# 或者检查响应中是否有admin相关字段变化
resp_text = resp.text.lower()
if 'is_admin' in resp_text or '"role":"admin"' in resp_text:
vulnerable_payloads.append(f"载荷{i+1}: 响应中包含admin属性")
except Exception:
pass
request_info = _build_request_info(
"POST", test_path,
json_data={"companyNumber": company_number, "pageSize": 5, "pageNum": 1,
"is_admin": True, "role": "admin"}
) + "\n攻击方式: 在请求体中注入is_admin/role/is_sso等特权属性"
if vulnerable_payloads:
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_HIGH,
description=f"用户查询接口存在API成批分配漏洞(CVSS 7.3),"
f"以下注入载荷成功影响了返回数据: {vulnerable_payloads}。"
f"攻击者可通过注入特权属性获取管理员级别数据",
request_info=request_info,
response_info=f"成功的载荷: {vulnerable_payloads}",
is_vulnerable=True,
fix_suggestion="1. 使用DTO明确绑定允许的字段,拒绝多余字段;"
"2. 在服务端过滤is_admin/role/roleId等敏感字段;"
"3. 从Token中提取用户角色,而非接受客户端传入"
)
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_HIGH,
description="API成批分配攻击未成功,接口参数过滤机制正常",
request_info=request_info,
response_info="所有注入载荷均未影响返回数据",
is_vulnerable=False
)
def test_2_3_9(client, auth):
"""
2.3.9: 运维集控越权访问 - company_id参数未校验 (HV-031)
来源: 天津海油渗透测试复测报告
测试接口: /monitor/api2/api/alarmststus/, roommaster/, devicemaintain/ 等
低权限用户通过修改company_id访问其他公司数据
"""
test_id = "2.3.9"
name = "运维集控越权-低权限用户通过company_id访问其他公司数据"
log.info(f"[{test_id}] {name}")
user_token = auth.get_user_token()
if not user_token:
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_INFO,
description="user Token 获取失败,跳过测试",
is_vulnerable=False
)
# 运维集控中通过company_id可能越权的接口(来自天津海油渗透测试确认的路径)
monitor_endpoints_with_company = [
("GET", "/monitor/api2/api/alarmststus/", {"company_id": "1"}),
("GET", "/monitor/api2/api/roommaster/", {"company_id": "1", "getmasterall": "1"}),
("GET", "/monitor/api2/api/devicemaintain/", {"company_id": "1", "pageSize": "10", "pageNum": "1"}),
("GET", "/monitor/api2/api/serverlog/", {"company_id": "1", "exportxml": "1"}),
]
vulnerable_endpoints = []
for method, path, params in monitor_endpoints_with_company:
request_info = _build_request_info(method, path, params=params)
request_info += f"\n使用 Token: user (低权限用户, company_id=1)"
resp = client.get(path, params=params, token=user_token)
if _is_success(resp):
# 验证确实返回了数据
try:
data = resp.json()
has_data = bool(data.get('results') or data.get('data') or
data.get('list') or data.get('records'))
if has_data:
vulnerable_endpoints.append(path)
except Exception:
vulnerable_endpoints.append(path)
request_info = _build_request_info(
"GET", "/monitor/api2/api/alarmststus/ 等",
params={"company_id": "1 (猜测其他公司ID)"}
) + "\n攻击方式: 低权限用户遍历company_id访问其他公司数据"
if vulnerable_endpoints:
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_HIGH,
description=f"运维集控接口存在越权漏洞,低权限用户通过company_id参数"
f"可访问其他公司数据。受影响接口: {vulnerable_endpoints}。"
f"(参考: 天津海油渗透测试确认的同类漏洞)",
request_info=request_info,
response_info=f"可越权访问的接口: {vulnerable_endpoints}",
is_vulnerable=True,
fix_suggestion="1. 从Token中提取用户的company_id,忽略客户端传入的company_id参数;"
"2. 所有运维集控接口必须校验当前用户所属公司;"
"3. 禁止低权限用户访问管理级别的运维集控接口"
)
return VulnResult(
test_id=test_id, name=name, level=VulnResult.LEVEL_HIGH,
description="运维集控接口company_id越权测试被正确拦截,权限控制正常",
request_info=request_info,
response_info="所有company_id越权尝试均被拒绝或返回空",
is_vulnerable=False
)
def run_tests(client, auth):
"""
执行 API3 所有对象属性级别授权失效测试用例
......@@ -655,6 +843,9 @@ def run_tests(client, auth):
results.append(test_2_3_6(client, auth))
results.append(test_2_3_7(client, auth))
# 新增:基于参考资料补充的测试用例
results.append(test_2_3_8(client, auth)) # HV-027 API成批分配 CVSS 7.3
results.append(test_2_3_9(client, auth)) # HV-031 运维集控越权company_id
log.info(f"API3 测试完成,共 {len(results)} 个用例")
return results
......@@ -72,6 +72,16 @@ def run_tests(client, auth):
# ============================================================
results.extend(_test_2_7_7(client, user_token))
# ============================================================
# 2.7.8: 桌牌同步SSRF (HV-024)
# ============================================================
results.append(_test_2_7_8(client, user_token, ssrf_payloads))
# ============================================================
# 2.7.9: SMTP邮件注入测试 (HV-023)
# ============================================================
results.append(_test_2_7_9(client, user_token))
log.info(f"API7 测试完成,共 {len(results)} 个用例")
return results
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论