安全测试-07-如何把基础安全校验接入自动化流程

一说“安全测试自动化”,很容易直接想到很重的东西:

  • 漏洞扫描平台
  • SAST / DAST 全家桶
  • 完整安全流水线

这些当然有价值,但对大多数业务团队来说,最先该做的往往不是这些,而是把一批最基础、最高频、最容易回归的安全校验先自动化起来。

因为很多安全问题的真实现场其实很朴素:

  • 以前修过的越权问题又回来了
  • 退出登录后旧 Token 又能用了
  • 领券接口改一次之后,幂等又失效了
  • 导出接口换了新版本,数据边界又松了

这些问题并不缺“安全概念”,缺的是稳定回归拦截

这篇文章就只讲一个更务实的问题:

哪些基础安全校验值得先自动化,怎么接到现有测试流程里。

一、先说结论:不是所有安全测试都适合自动化

安全测试自动化最容易走偏的地方,是一上来就想把所有事情都自动化。

更倾向先按三类分开看:

1. 非常适合自动化的

这类问题通常具备几个特点:

  • 输入固定
  • 预期明确
  • 结果可断言
  • 容易回归

例如:

  • 未登录访问关键接口应返回 401/403
  • 普通用户访问管理员接口应被拒绝
  • 退出登录后旧会话应失效
  • 批量接口不能操作非授权资源
  • 一次性动作重复请求不能重复生效

2. 适合半自动化的

这类问题通常需要:

  • 固定 payload
  • 固定环境
  • 人工做最终判断

例如:

  • 富文本 XSS 验证
  • 文件上传边界验证
  • 某些复杂 CSRF 验证

3. 暂时不适合直接自动化的

例如:

  • 高度依赖人工分析上下文的复杂业务绕过
  • 需要现场探索的组合型攻击链路
  • 环境波动极大的专项安全测试

这类就不要硬往日常流水线里塞。

二、更推荐先自动化哪几类基础安全校验

如果从投入产出比看,最适合优先自动化的是下面 5 类。

1. 未授权访问校验

这类最适合做成接口自动化。

典型断言:

  • 不带 Token 请求关键接口
  • 应返回未授权
  • 不应泄露业务数据

2. 越权与权限边界校验

例如:

  • 普通用户不能看别人的订单
  • 普通用户不能调用审批通过接口
  • 非管理员不能导出全量数据

这类校验的核心不是接口数量多,而是挑出高风险动作做稳定拦截。

3. 会话失效校验

例如:

  • 退出登录后旧请求应失效
  • 改密后旧 Token 应失效
  • 角色降权后旧高权限请求应失效

这类问题非常适合做成回归脚本,因为它们特别容易被后续改动带回来。

4. 幂等与重放校验

例如:

  • 领券请求重复提交只能生效一次
  • 审批接口重复请求不能重复落库
  • 支付回调重复调用不能重复改状态

5. 响应最小暴露校验

例如:

  • 未授权请求不应返回完整业务详情
  • 错误响应不应直接泄露内部堆栈、SQL、路径信息

这类校验实现不复杂,但很实用。

三、更常用的落地方式:不要单独造安全体系,先挂到现有自动化框架

把“安全自动化”想得太独立,结果推进成本很高。
更常用的方法是:

  • 能接到接口自动化里的,就接到接口自动化里
  • 能接到 UI 巡检里的,就接到 UI 巡检里
  • 能接到 CI/CD 的,就挂到 CI/CD 里

这样成本最低,也最容易真正跑起来。

1. 接口自动化是最好的入口

因为很多基础安全校验,本质上就是:

  • 换身份
  • 换资源 ID
  • 去掉鉴权头
  • 重放旧请求

这些动作和接口自动化天然契合。

例如完全可以在现有接口框架里加一组用例:

  • test_order_detail_unauthorized
  • test_order_detail_horizontal_privilege
  • test_export_role_forbidden
  • test_logout_token_invalid

2. UI 自动化适合补“页面是否真的兜住”

例如:

  • 退出登录后,页面回退是否还能继续调接口
  • 富文本输入后是否在高权限页面触发脚本
  • 某些关键按钮虽然隐藏了,但接口层是不是仍能执行

这类适合放在关键路径巡检里,不适合铺太广。

3. CI/CD 只挂基础稳定项

流水线最怕的是不稳定。

所以我只建议把下面这些放进 CI/CD:

  • 未授权访问
  • 越权核心场景
  • 会话失效
  • 幂等 / 重放

不要把大量易波动、依赖环境的专项安全测试直接塞进主流水线。

四、一套更实用的最小接入骨架

如果要在现有测试体系里加基础安全回归,建议至少固定下面这套骨架。

1. 先列一张“安全回归白名单”

不要一上来全量接口都做。

通常会先收这些高风险接口:

接口/动作 风险类型 是否自动化
查看详情 未授权 / 同级越权
审批通过 垂直越权 / 重放
导出数据 权限边界
修改手机号 会话 / CSRF 视情况
领券动作 重放 / 幂等
文件上传 上传边界 半自动

2. 每类问题抽成统一动作

例如在接口框架里抽公共方法:

  • without_auth()
  • with_user_a_token()
  • with_user_b_token()
  • replay_request()
  • replace_resource_id()

这样每条安全用例就不会写得很散。

3. 固定断言结构

安全自动化不要只断言状态码。

至少同时断言:

  • HTTP 状态码
  • 响应语义
  • 业务数据是否泄露
  • 业务状态是否变化

例如越权审批场景更合理的断言应该是:

  • 返回 403
  • 响应提示无权限
  • 审批状态保持不变

4. 固定前置数据和角色关系

安全用例非常依赖数据关系。

至少要固定:

  • 普通用户 A
  • 普通用户 B
  • 高权限用户 C
  • A 自己的资源
  • B 自己的资源
  • 可操作与不可操作的任务数据

如果这些前置关系不稳定,自动化很容易漂。

五、一条最小可执行示例:把越权校验接到接口自动化里

如果现在就要做,更推荐从最小的例子开始。

例如一个订单详情接口:

1
2
3
4
5
6
7
def test_order_detail_horizontal_privilege(api_client, user_a_token, user_b_order_id):
response = api_client.get(
f"/api/order/detail?orderId={user_b_order_id}",
headers={"Authorization": f"Bearer {user_a_token}"}
)
assert response.status_code in (403, 404)
assert "orderNo" not in response.text

如果是会话失效校验,也可以写成:

1
2
3
4
5
6
7
def test_logout_token_invalid(api_client, login_and_logout):
token = login_and_logout["token"]
response = api_client.get(
"/api/user/profile",
headers={"Authorization": f"Bearer {token}"}
)
assert response.status_code in (401, 403)

这类用例并不复杂,但拦截价值很高。

六、接入流水线时最容易踩的几个坑

1. 把专项安全测试全塞进日常流水线

结果通常是:

  • 跑得慢
  • 不稳定
  • 报警太多

最后团队很快就把这批检查静音了。

2. 没有稳定测试数据

安全用例比普通接口回归更依赖角色和资源边界。
如果数据关系不固定,越权用例最容易误报。

3. 只断状态码,不断业务结果

比如重放请求第二次返回失败,但数据库其实已经被改了两次。
如果只看状态码,就会漏掉问题。

4. 不区分“阻断型”和“观测型”安全用例

更合适的做法是分两层:

  • 阻断型:核心未授权、越权、会话失效、幂等问题
  • 观测型:上传边界、特殊输入、异常暴露等

前者可以阻断发布,后者更适合告警或专项回归。

七、真实案例:为什么修过的导出越权问题三个月后又回来了

场景

一个后台系统里,之前修过一次“普通运营可导出其他部门数据”的问题。
当时研发已经在导出接口里补了数据范围校验,问题关闭。

执行

三个月后,导出模块重构。
测试在版本回归时手工没有再重点看这条链路,但我保留的一条安全回归脚本还在跑:

  • 普通运营账号登录
  • 请求导出接口
  • deptId 换成其他部门
  • 校验导出文件是否包含越权数据

现象

脚本再次报错。
进一步确认后发现:

  • 新版本导出接口改成了新的服务实现
  • 新服务只校验了“是否有导出功能权限”
  • 没把原先的数据范围校验迁过去

也就是说,这个老问题不是“又出现了新形态”,而是修复逻辑在重构时丢了。

排查

因为这条用例已经标准化了,排查过程很快:

  • 前置账号和数据固定
  • 请求样例固定
  • 断言目标固定
  • 报错信息直接指向导出结果越权

研发几乎不需要重新理解业务背景,就能快速定位差异点。

修复

最后的修复动作很直接:

  1. 在新导出服务补回数据范围校验
  2. 将这条越权回归用例保留为阻断型流水线检查
  3. 补一条“管理员可导出、普通运营不可导出其他部门”的对照用例

这个案例很能说明自动化的价值:

很多基础安全问题不是发现一次就结束,而是要靠持续回归防止它被重构带回来。

八、怎么把这件事做成团队长期机制

如果要长期跑起来,建议至少固定这几条。

1. 每次安全问题修复后,判断是否值得转成自动化回归

判断标准很简单:

  • 是否高频
  • 是否可稳定复现
  • 是否可明确断言

只要满足,就尽量转成脚本。

2. 建立一份“安全回归清单”

至少包含:

  • 接口名
  • 风险类型
  • 对应用例
  • 是否阻断发布

3. 分层接入

  • 日常接口回归:跑核心安全边界
  • 合并前检查:跑高价值阻断项
  • 定时专项任务:跑半自动或较重安全项

这样不会把主流水线拖垮。

九、写在最后

基础安全校验接入自动化,真正有价值的地方,不是让团队看起来“更安全化”,而是把那些最容易反复回来的老问题真正钉住。

说到底,这件事最值得先做的,不是求全,而是先把几类高频问题守住:

  • 未授权访问
  • 越权
  • 会话失效
  • 幂等和重放

只要这几类能稳定拦住,安全自动化这件事就已经开始产生真实收益了。