3. 安全合规错误码分类

在集成测试环境下,后端接口返回的 HTTP 状态码与业务错误码是安全合规断言的核心依据。错误码的准确分类不仅决定了测试用例的断言逻辑,还直接关联到安全审计与合规扫描的通过率。本章将错误码划分为四大类:认证类、授权类、输入校验类与资源不存在类,并逐一分析其语义、断言策略与常见陷阱。

3.1 认证类错误码(401 / 403)

认证类错误码是安全合规中最基础也最容易混淆的一类。HTTP 规范明确区分了 401 Unauthorized 与 403 Forbidden,但在实际项目中常被误用或混用。集成测试断言必须严格遵循语义差异。

状态码 语义 典型场景 断言要点
401 未认证(Unauthenticated) 缺少 Token、Token 过期、Token 格式错误 响应体应包含 error_code 如 AUTH_TOKEN_MISSING;WWW-Authenticate 头必须存在
403 已认证但无权限(Forbidden) 用户角色不足、IP 白名单拒绝、请求被 WAF 拦截 响应体应包含 error_code 如 FORBIDDEN_ROLE;不应返回 WWW-Authenticate 头
合规提示: 安全审计标准(如 OWASP ASVS)要求 401 与 403 的响应体结构必须一致,避免攻击者通过响应差异推断资源是否存在。集成测试应断言两个状态码返回的 JSON 字段数量与格式相同。
集成测试断言示例(Python + requests):
def test_401_response_structure():
    resp = client.get("/api/secure", headers={})
    assert resp.status_code == 401
    assert "error_code" in resp.json()
    assert "message" in resp.json()
    assert "WWW-Authenticate" in resp.headers

def test_403_response_structure():
    resp = client.get("/api/admin", headers={"Authorization": "Bearer user_token"})
    assert resp.status_code == 403
    assert "error_code" in resp.json()
    assert "message" in resp.json()
    assert "WWW-Authenticate" not in resp.headers
认证类错误码决策流程 收到 API 请求 是否携带有效 Token? 否 401 Unauthorized 是 检查权限 403 Forbidden 401:未认证,需提供凭证 403:已认证,但权限不足

3.2 授权类错误码

授权类错误码与 403 有重叠,但授权错误更聚焦于业务权限而非通用访问控制。常见授权错误码包括 403(通用无权限)、451(法律原因拒绝)以及自定义业务错误码如 INSUFFICIENT_PERMISSION、ROLE_NOT_ALLOWED。集成测试需要区分“功能级授权”与“数据级授权”。

  • 功能级授权: 用户角色是否允许执行某个操作(如删除用户)。断言:返回 403 + 业务错误码 FUNCTION_FORBIDDEN。
  • 数据级授权: 用户是否只能操作自己拥有的数据(如只能查看自己的订单)。断言:返回 403 或 404(防止枚举),取决于安全策略。
  • 跨域授权: OAuth2 Scope 不足时返回 403 或 insufficient_scope 错误。
授权类型 HTTP 状态码 业务错误码示例 断言策略
功能级授权 403 FUNCTION_FORBIDDEN 断言 status_code == 403,error_code 匹配预期
数据级授权 403 / 404 DATA_ACCESS_DENIED 根据安全策略断言是否返回 404 隐藏资源存在性
OAuth2 Scope 403 insufficient_scope 断言响应包含 error 字段且值为 insufficient_scope
常见陷阱: 某些框架在数据级授权失败时返回 404 Not Found 而非 403,这是为了防止攻击者通过错误码差异推断资源是否存在。集成测试必须与安全策略文档对齐,不能硬编码为 403。

3.3 输入校验类错误码

输入校验错误码覆盖参数缺失、格式错误、类型不匹配、长度超限、SQL 注入特征检测等场景。安全合规要求输入校验错误不能泄露内部实现细节(如数据库字段名、堆栈信息)。

  • 标准状态码: 400 Bad Request 是输入校验错误的主状态码;422 Unprocessable Entity 用于语义错误(如 JSON 结构正确但业务逻辑无效)。
  • 业务错误码规范: 推荐使用 VALIDATION_ERROR 作为通用错误码,配合 field 与 reason 字段定位具体问题。
  • 安全约束: 错误信息不得包含正则表达式细节、数据库列名、文件路径或 SQL 片段。
输入校验错误响应体规范示例:
{
  "error_code": "VALIDATION_ERROR",
  "message": "请求参数校验失败",
  "details": [
    {
      "field": "email",
      "reason": "格式不正确",
      "code": "INVALID_FORMAT"
    },
    {
      "field": "age",
      "reason": "必须为整数且大于0",
      "code": "OUT_OF_RANGE"
    }
  ]
}
校验类型 HTTP 状态码 业务错误码 安全合规要求
必填参数缺失 400 MISSING_REQUIRED_PARAM 不暴露参数名以外的信息
格式校验失败 400 INVALID_FORMAT 不输出正则表达式
业务逻辑校验 422 BUSINESS_VALIDATION_ERROR 不输出数据库查询细节
安全特征检测 400 MALICIOUS_INPUT_DETECTED 不输出触发规则的具体关键词

3.4 资源不存在类错误码

资源不存在类错误码的核心安全考量是信息泄露防护。标准做法是统一返回 404 Not Found,无论资源是不存在还是无权限访问。集成测试需要验证响应体的一致性,避免通过错误码差异泄露资源存在性。

  • 标准状态码: 404 Not Found 是唯一推荐的状态码。
  • 业务错误码: 推荐使用 RESOURCE_NOT_FOUND 或 NOT_FOUND,不区分“用户不存在”与“订单不存在”。
  • 特殊场景: 某些 RESTful 设计在删除资源时返回 204 No Content,但若资源已不存在则仍应返回 404。
集成测试最佳实践: 编写一个通用测试用例,遍历所有 GET/PUT/DELETE 接口,使用不存在的 ID 请求,断言所有接口返回相同的状态码(404)和相同的错误码结构。这能有效防止信息泄露漏洞。
资源不存在错误码安全策略对比 ❌ 不安全策略:差异化错误码 用户不存在 → 404 + "USER_NOT_FOUND" 订单不存在 → 404 + "ORDER_NOT_FOUND" 攻击者可枚举判断资源类型 ✅ 安全策略:统一错误码 任何资源不存在 → 404 + "RESOURCE_NOT_FOUND" 无权限访问 → 404 + "RESOURCE_NOT_FOUND" 攻击者无法区分不存在与无权限 集成测试必须强制统一 404 响应结构

3.5 错误码分类总结与断言矩阵

下表汇总了四类错误码的 HTTP 状态码、业务错误码前缀、安全合规要点与集成测试断言策略,供测试用例设计时直接参考。

错误码分类 HTTP 状态码 业务错误码前缀 安全合规要点 断言策略
认证类 401, 403 AUTH_, TOKEN_ 401 必须带 WWW-Authenticate 头;401 与 403 响应体结构一致 断言状态码、头字段、错误码前缀
授权类 403, 451 FORBIDDEN_, SCOPE_ 区分功能级与数据级授权;数据级授权可能返回 404 断言状态码 + 业务错误码 + 权限上下文
输入校验类 400, 422 VALIDATION_, INVALID_ 错误信息不泄露内部实现;使用统一错误码结构 断言 details 数组结构与字段名
资源不存在类 404 NOT_FOUND, RESOURCE_ 统一错误码,不区分资源类型;无权限也返回 404 断言所有接口返回相同 404 结构
集成测试通用断言模板:
def assert_error_response(resp, expected_status, expected_error_prefix):
    assert resp.status_code == expected_status
    body = resp.json()
    assert "error_code" in body
    assert body["error_code"].startswith(expected_error_prefix)
    assert "message" in body
    # 安全合规:响应体不应包含堆栈或 SQL
    assert "Traceback" not in resp.text
    assert "SELECT" not in resp.text
    assert "INSERT" not in resp.text

通过以上分类与断言策略,集成测试可以在安全合规层面建立统一的错误码断言基线,有效降低因错误码差异导致的信息泄露风险,同时提升测试用例的可维护性与可审计性。