# 安全验收（SSAT）

> 上线或大版本发布前，逐项用证据核对 SSAT。

工程防护 · /zh/ssat/ · 2026-11-05

系统安全验收测试（System Security Acceptance Test，SSAT）是 Neutron 针对单个系统的安全检查表。

## 让 AI agent 核对 SSAT

**Codex**

```text
$neutron:ssat-review 按 [SSAT 文件] 中的 SSAT 核对 [系统或仓库]，并加上客户的要求：[客户检查表，没有就写“无”]。
逐项判断它的描述是否适用于这个系统。对适用的检查项，用只读权限查看代码、CI 配置和已部署的设置，逐行核对。
每一行都记录证据：文件和行号、CI 运行、配置值或命令输出。然后为每项给出建议的 Status：Closed；RA，写明缺口和风险；或 NA，写明原因。
不要改代码、设置或基础设施。
```

**Claude Code**

```text
/ssat-review 按 [SSAT 文件] 中的 SSAT 核对 [系统或仓库]，并加上客户的要求：[客户检查表，没有就写“无”]。
逐项判断它的描述是否适用于这个系统。对适用的检查项，用只读权限查看代码、CI 配置和已部署的设置，逐行核对。
每一行都记录证据：文件和行号、CI 运行、配置值或命令输出。然后为每项给出建议的 Status：Closed；RA，写明缺口和风险；或 NA，写明原因。
不要改代码、设置或基础设施。
```

## 每一行都填上证据

- **SSAT-1** (高风险; 由代码评审检查): **每条适用的 SSAT 检查项都要有证据支撑。** 记录文件和行号、CI 运行、配置值、命令输出或测试结果，而不是光写一个“Yes”。每个缺口要么在上线前修复，要么记为风险接受并写明理由。

* 先在 Applicability 里说明这一项是否适用。填“No”要用一句话说明原因，比如“没有用户文件上传”。
* 在 Remarks 里，给检查表的每个编号行分别写证据。核对的是已部署的系统，不是设计意图：设置、CI 运行记录和线上响应比架构图或 ticket 更有说服力。
* 填这一项的 Status：每一行都满足时填 Closed，仍有缺口时填 RA（风险接受，risk acceptance），不适用时填 NA。
* 填 RA 时，在 Justification 里写清缺什么、有什么风险、谁接受了风险，以及后续关闭它的 ticket。既没修复也没被接受的缺口会阻止上线；AI agent 可以建议 RA，但只有人能接受风险。
* 登录、数据、基础设施或集成发生重大变化后，重新核对受影响的检查项。

> **示例: 同一行 SSAT 的两种记法**
>
> | 检查行                   | Remarks                                                          | 说明                    |
> | --------------------- | ---------------------------------------------------------------- | --------------------- |
> | RM-001 · 5. CI 中的密钥扫描 | 5. Yes                                                           | 无法核实；扫描可能存在，但只报告、不拦截。 |
> | RM-001 · 5. CI 中的密钥扫描 | 5. CI 工作流里的 `secrets` 任务发现任何问题都会让 PR 失败；`7d03f5b` 上的运行 4821 已通过。 | 任何人都能打开这次运行，确认它确实会拦截。 |

## 检查发布管理（RM）

- `RM-001` 从仓库设置里读分支规则，而不是看文档：`main` 和 `release/*` 要求评审和检查通过，禁止直接 push，管理员也不能绕过。
- `RM-001` SAST、依赖扫描和密钥扫描在每个 PR 上都会运行，发现严重问题就让 PR 失败。只报告、不拦截的扫描算缺口。依赖变更还要过[依赖检查](/zh/security/#r-sec-2)。
- `RM-001` 确认上次 SSAT 以来合并的每个 PR 都跑过 [`pr-review`](/zh/code-review/)，并有人工批准，且评审覆盖了这份检查表中与代码相关的项。

## 检查个人数据（DS）

- `DS-001` 列出每个个人数据字段及收集它的理由，没有理由的字段就删掉。
- `DS-001` 从数据库和存储的设置确认传输加密和静态加密，不要想当然地依赖默认值。
- `DS-001` 用合成个人数据触发一个错误，再到日志和错误报告里搜索这些数据。它们必须是脱敏的，或者根本不出现。
- `DS-001` 用两个账号测试涉及个人数据的接口，方法同[访问、密钥与数据](/zh/security/#test-denied-access)。
- `DS-001` 记录数据及其备份存放在哪里，包括第三方处理方，并与项目要求的区域对照。

## 检查基础设施和互联网暴露面（SS-001、SS-002）

- `SS-001` 从网络外部确认数据库和内部服务都连不上。导出防火墙或安全组规则，检查默认策略是拒绝。
- `SS-001` 记录最近一次防火墙规则审查的日期；没有日期就算缺口。
- `SS-002` 确认每个公开端点（包括 API）都在 WAF 和 DDoS 防护之后。
- `SS-002` 对 Web 应用和 API 分别运行 `curl -sI https://[host]`。两个响应都要有 HSTS、X-Content-Type-Options、CSP（带 `frame-ancestors`，或使用 X-Frame-Options）和 Referrer-Policy，并且不暴露服务器版本。
- `SS-002` 限流在应用里实现，做法同[短信、OTP 与付费接口](/zh/otp/#check-requests-before-sending)。

## 检查登录和管理员访问（SS-003、SS-004）

- `SS-003` 写明使用的身份提供方。自己实现密码或 token 处理，要有书面理由。
- `SS-003` 移动端和浏览器应用使用带 PKCE 的 OAuth；不用 implicit flow。
- `SS-003` 用测试账号反复输错密码，直到触发锁定或延迟，记录阈值。
- `SS-003` 从配置里记录 access token 和 refresh token 的实际有效期，并测试被吊销的 refresh token 确实失效。
- `SS-003` 连续申请两次密码重置，然后用第一封邮件的链接：必须失败。确认重置链接 15 分钟内过期，并且不会出现在日志记录的 URL 里。
- `SS-004` 列出管理员账号：只能是实名个人，每个都开启 MFA，每次管理员操作都有审计日志。记录最近一次访问审查的日期。

## 检查密钥和证书（SS-005）

- `SS-005` 每个密钥都从 vault 或密钥管理服务读取；在代码和 CI 配置里搜索，找出没这样做的。
- `SS-005` 记录每个密钥的轮换周期和最近一次轮换日期。
- `SS-005` 确认证书过期告警至少提前 30 天触发。
- `SS-005` 运行 `nmap --script ssl-enum-ciphers -p 443 [host]`。不能出现 TLS 1.0、TLS 1.1 和 SSL。

## 检查 API、输入和上传（SS-006 至 SS-008）

- `SS-006` 找到集中决定权限的那个模块或中间件。让 AI agent 审查它有没有权限提升、缺少范围检查、能访问其他用户记录的问题，并用两个账号测试。
- `SS-006` 只有在每次查询都[校验记录所有者](/zh/security/#r-sec-1)时，才能用自增 ID。记录属于哪种情况。
- `SS-006` 在 staging 故意制造一个错误：响应只给通用提示，不带堆栈或内部路径。
- `SS-006` 对 API 的明文 HTTP 请求会被拒绝。服务间调用方除了凭据，还要按 IP 限制。
- `SS-007` 在服务端做校验和权限检查；只有前端检查不算通过。在代码里搜索用字符串拼接的 SQL，以及传给 shell 命令的用户输入。
- `SS-007` 确认每个改变状态的请求都有 CSRF 防护，渲染用户文本的地方都做了输出编码。
- `SS-008` 上传一个改了扩展名的禁用类型文件和一个超大文件，两者都必须被拒绝。确认有恶意软件扫描，并且文件只能通过授权下载获取。

## 检查测试和第三方服务（SS-009、SS-010）

- `SS-009` VAPT 由独立且有资质的供应商执行，覆盖基础设施、应用和 API。上线前每个发现都已修复或已风险接受；附上报告。
- `SS-010` 维护所有第三方服务的清单：用途、它收到哪些数据、怎么认证。
- `SS-010` 在 staging 屏蔽其中一个服务。系统按设计处理这个故障，告警也会触发。

## 检查费用、支付和群发（FS、CS）

- `FS-001` 付费调用对照[短信、OTP 与付费接口](/zh/otp/)检查，幂等对照[资金与奖励](/zh/value-abuse/#r-val-3)检查。记录谁负责核对服务商账单，多久核对一次。
- `FS-002` 确认不存储卡号或 CVV，退款和冲正都测试过，每笔交易都有审计记录。
- `CS-001` 确认只有授权角色能触发群发，并把生产环境中实际拥有这个权限的人和项目的访问策略对照。
- `CS-001` 从 staging 发一次测试群发。它只送达测试联系人，使用单独的凭据和发送方 ID，并带 `[UAT]` 前缀。
