安全应用脚手架 是做什么的?
生成符合ANSSI、CIS、FBI和OWASP ASVS Level 2标准的安全优先应用:TypeScript前端、Python后端、强化版PostgreSQL。
布局、README和UI遵循 project-blueprint 技能。本技能中的安全规则优先于任何便捷性或UX规则。
参考文件(生成匹配产物时读取)
- reference/docker-compose.yml:强化版Compose配置,包含密钥引导程序。
- reference/nginx.conf:反向代理配置,包含TLS 1.3、混合后量子密码、安全头、速率限制。
- reference/security-tests.md:强制性安全测试矩阵。
使用时机
- 启动、搭建或架构新的Web应用或服务。
- 需求中提及ANSSI、CIS、FBI、OWASP、强化、无根、合规性。
- 添加身份验证、授权、文件上传、审计日志、管理功能、部署文件或MCP端点。
部署目标: 仅限Docker和无根Podman。除非用户明确要求,否则不要生成Kubernetes、Helm或其他编排器清单。
0. 威胁模型
- 创建
docs/security/threat-model.md(≤1页):包含资产、信任边界、角色、主要风险(STRIDE)以及缓解每个风险的控制措施。当出现新的信任边界(如上传、Webhook、LLM、MCP)时更新。
1. 容器与运行时
- 无根模式: 每个镜像都以数字
USER(例如USER 10001:10001)结尾,包括Web服务器(nginxinc/nginx-unprivileged,端口 > 1024)。 - 最小化、固定版本镜像: 多阶段构建;运行阶段为distroless或精简镜像,无shell、编译器或包管理器;排除开发依赖。基础镜像按摘要固定。使用最新的稳定版Python。
- 强化: 启用
read_only、为可写路径挂载tmpfs、cap_drop: [ALL]、no-new-privileges、使用运行时默认seccomp配置文件(从不使用unconfined)、设置pids_limit、内存限制、健康检查、禁用核心转储(ulimits: core: 0)。只有反向代理发布端口;数据库网络设置为internal: true。永不挂载Docker/Podman套接字。 - 无子进程: 不使用
subprocess、os.system、shell=True。通过以下方式强制执行:镜像中无shell、SAST规则、以及每个工作进程启动后加载的进程内seccomp过滤器,阻止execve/execveat(容器级别的拒绝会阻止入口点本身启动)。 - 首次运行保证: 在干净的克隆上,
docker compose up(也包括docker-compose up和podman compose up)首次即可工作:无需.env、无需手动步骤、除容器引擎外无其他先决条件。镜像自动构建(build:),仅使用Docker Compose v2和podman-compose支持的Compose Specification功能,不使用仓库外的主机路径,发布端口 > 1024,不设置CPU限制(在无根cgroup设置中不可用)。make up是同一命令的轻量级包装。 - 无硬编码密钥: 一次性
init服务将密钥生成到命名卷(0600)中,通过*_FILE变量消费。在生产环境中,密钥从文件而非环境变量读取。 - 隔离环境: 所有JS/CSS/字体/图标均为本地打包;无CDN、运行时下载或遥测。离线构建时,依赖来自内部镜像仓库(Nexus、Artifactory、devpi、Verdaccio)。
- Vault就绪: 单一密钥提供者抽象(默认为
file,生产环境使用vault/sops)。 - 内部TLS: 后端 ↔ 数据库之间使用TLS;服务之间可选mTLS。
2. 供应链与扫描
- 锁定依赖: 提交锁定文件;使用
npm ci、uv sync --frozen或pip install --require-hashes。配置.npmrc使用ignore-scripts=true,除非某个包需要安装脚本(使用白名单)。 - 预构建门禁:
npm audit --audit-level=high和pip-audit在退出码 > 0 时使构建失败。 - SAST与密钥扫描: pre-commit钩子使用
ruff、bandit/semgrep、eslint(+eslint-plugin-security)、gitleaks。 - 镜像扫描(
make scan):trivy image(CVE + 密钥 + 错误配置)、在Dockerfile和Compose文件上执行trivy config、针对基础操作系统的CIS配置集执行oscap-podman(或oscap-docker)。 - SBOM与签名(
make sbom): CycloneDX SBOM(使用Trivy或Syft生成),使用cosign签名镜像(离线环境使用密钥),SBOM作为认证附加;部署时验证签名。 - DAST(可选,推荐): 在CI中针对运行中的堆栈执行OWASP ZAP基线扫描。
- 更新: 在固定节奏上使用Renovate/Dependabot(或等效的离线审查)。
3. 前端(TypeScript)
- 位置:
/frontend目录,包含自己的Dockerfile,构建为静态资源,由非特权Nginx提供服务。 - CSP兼容代码: 无内联
<script>、无eval/new Function、无运行时CSS-in-JS注入不带nonce的<style>。优先使用编译后的CSS。启用Trusted Types(require-trusted-types-for 'script')。 - 零信任输入: 每个表单使用类型化Zod模式,与API类型共享。不对不可信数据使用
dangerouslySetInnerHTML/v-html;如果富文本无法避免,则使用DOMPurify。 - 幂等性: API客户端为每个POST/PUT/PATCH/DELETE请求附加一个
Idempotency-Key(UUIDv4),每个用户操作生成一次,重试时重用。 - CSRF防护: 自定义头(
X-Requested-With)+ CSRF令牌;后端还检查Origin/Sec-Fetch-Site。CORS仅限白名单,绝不对携带凭据的请求使用*。 - 开放重定向防护: 登录后的
next/returnUrl仅接受以单个/开头的相对路径。 - 捆绑包中无密钥; 生产中无源码映射;
localStorage/sessionStorage中不存储敏感数据。
4. 后端(Python)与数据库
- 验证: 每个请求体、查询参数、路径参数和请求头均由Pydantic验证,包含最大长度和
extra="forbid"(防止批量赋值:role、owner_id、is_admin永远不能从输入中绑定)。 - 资源限制: 最大请求体大小、最大JSON深度/数组长度、最大分页大小(例如100)、服务端超时。用户输入的正则表达式需有界或使用线性时间引擎(
re2)以避免ReDoS。 - 安全解析: 不使用
pickle、marshal、yaml.load(使用yaml.safe_load)、eval。XML使用defusedxml。模板源码中不包含用户输入(SSTI)。文件路径需解析并对照基目录检查(路径遍历)。 - 幂等性存储:
(user_id, key) -> request_hash, status, response,TTL为24小时。相同key + 相同hash返回存储的响应;不同hash返回422。这防止了重复请求;重放防护来自TLS、CSRF令牌和短会话。 - SQL: 仅使用ORM或参数化查询;动态标识符(
ORDER BY、列名)通过白名单控制。 - 列级加密: PII在应用内部使用AES-256-GCM加密,采用信封加密(每条记录的DEK由来自密钥提供者的主密钥包裹),设置
key_version列用于轮换,对可搜索字段使用HMAC-SHA256盲索引。主密钥绝不存储在数据库或数据库备份中。 - 数据库强化: 最小化PostgreSQL,使用
scram-sha-256认证、TLS、两个角色(迁移所有者、运行时仅DML角色)、应用无超级用户权限、Alembic迁移、运行时角色设置statement_timeout。 - SQL控制台(如果有): 只读角色、仅允许单条
SELECT、设置statement_timeout、行数限制、锁定配置。 - 加密与后量子: 静态AES-256-GCM具有量子抗性。后量子就绪性针对密钥交换和签名:代理上使用混合TLS
X25519MLKEM768;仅在库支持的情况下使用ML-KEM/ML-DSA(FIPS 203/204)。随机值来自secrets/crypto.getRandomValues。绝不自行实现加密原语。 - 出站请求(SSRF): 任何UI可配置的URL(Webhook、syslog、LLM、OIDC)都经过验证:仅允许
https(除非明确白名单),解析后的IP如果是回环/链路本地/元数据地址(169.254.0.0/16、fd00:ec2::254)或私有地址(除非白名单)则拒绝,DNS解析后重新检查(DNS重绑定),不进行跨主机重定向,严格的超时。 - 出站Webhook: 使用HMAC-SHA256签名(
X-Signature+ 时间戳),以便接收方验证。 - 电子邮件: 头信息值剥离CR/LF;使用带证书验证的SMTPS。
- 导出: CSV/XLSX中以
=、+、-、@、制表符或CR开头的单元格前面加上'(防止公式注入)。 - 速率限制: 登录、密码重置、MFA验证、API密钥、导出、上传和高成本端点。
- 响应: 已验证的API响应设置
Cache-Control: no-store。使用包含request_id的通用错误消息;堆栈跟踪仅记录在日志中。生产中禁用OpenAPI UI或仅限管理员使用。
5. 授权
- 默认拒绝: 每个路由声明其所需权限;未声明的路由会在启动或测试时失败。
- RBAC: 使用映射到权限的角色(而非直接在代码中检查角色)。
- 对象级检查(IDOR/BOLA): 在仓库层对每个按ID进行的读/写操作执行所有权或租户检查。对现有对象的未授权访问返回
404。 - 功能级检查: 管理路由位于
/api/v1/admin/*下,由单个依赖守卫,绝不通过仅隐藏UI来保护。 - 敏感操作(角色变更、密钥创建、导出、删除用户)需要最近重新认证;角色提升为管理员时可选的“四眼”审批。
6. 文件上传(如果有)
- 在代理和应用层面设置大小限制;扩展名白名单和魔数检查。
- 存储在Web根目录之外(或对象存储中),随机文件名,使用
Content-Disposition: attachment和X-Content-Type-Options: nosniff提供服务。 - 图像重新编码(剥离EXIF和图片内嵌文件);存档文件检查炸弹攻击和路径遍历;可选离线防病毒(ClamAV)。
- 除非经过清理并从隔离源提供服务,否则拒绝SVG和HTML上传。
7. 日志、审计与管理
- 结构化日志: JSON格式,级别为DEBUG/INFO/WARN/ERROR/CRITICAL,处处包含
request_id,使用UTC时间戳,从用户值中剥离CR/LF。绝不记录密码、令牌、密钥、会话ID。 - 远程Syslog: 使用TLS(RFC 5425)的RFC 5424协议,带有有界本地缓冲区;主机、端口、TLS、CA可在管理UI中配置。无rsyslog容器。
- 错误通知: SMTPS和Webhook可从管理UI配置,经过SSRF验证,包含“发送测试”按钮,可限流。
- PII屏蔽: 可从管理UI切换的中间件。
- 防篡改审计追踪: 仅追加表(运行时角色仅拥有
INSERT/SELECT权限),每行与前一行的哈希链接,转发到syslog。涵盖登录、失败、MFA变更、角色变更、配置变更、导出、API密钥、蜜罐命中;记录用户、IP、用户代理、UTC时间以及变更前后的值。 - 管理仪表盘: 使用统计、错误、蜜罐告警、被封禁的IP。
- 计划性清理: 禁用不活跃账户、清除过期令牌/会话/幂等性键、每日加密备份(密钥与备份分离)、保留策略以及经过测试的恢复流程(
make restore-test)。 - 个人数据(GDPR): 每种数据类型的数据保留期限、用户数据导出和删除、清理任务。
8. 身份验证、身份与主动防御
- 密码: Argon2id(使用argon2-cffi的默认值 / RFC 9106,最小 m=19 MiB,t=2,p=1),参数更改时在登录时重新哈希。密码策略遵循NIST SP 800-63B:≥ 12个字符,最大 ≥ 64个字符,无组合规则,如果存在于捆绑的已泄露/常见密码列表中则拒绝。
- 登录: 对未知用户返回相同的响应和时间,逐步延迟 + 按账户和按IP限流(避免导致拒绝服务攻击的硬锁定)。
- 密码重置: 一次性随机令牌,以哈希形式存储,30分钟有效期,无论账户是否存在都返回相同响应,重置后撤销所有会话。
- 会话: 服务端会话,cookie为
__Host-session(HttpOnly; Secure; SameSite=Lax; Path=/),登录和权限变更时轮换ID,用户可以列出和撤销其活跃会话。浏览器存储中不使用JWT;如果服务间使用JWT:算法固定,拒绝none,检查aud/iss/exp,短生命周期。 - 管理员会话: 空闲1小时,绝对时间8小时,破坏性操作需要重新认证。
- MFA: TOTP(哈希恢复码,拒绝重放相同代码)和WebAuthn/Passkeys。管理员账户必须启用MFA。
- 上下文检测: 从未知IP/设备的管理员登录会触发“新登录”通知,并在MFA确认前限制破坏性权限。
- 引导管理员: 首次启动时生成随机密码,可通过
make admin-password查看一次,首次登录时强制重置密码并注册MFA。 - 紧急账户(可选): 当SSO不可用时使用的本地管理员账户,默认禁用,每次使用都会触发严重告警。
- SSO: OIDC授权码流程 + PKCE,验证
state和nonce,检查ID令牌签名/aud/iss;LDAPS或带证书验证的StartTLS,LDAP过滤器值已转义(防止LDAP注入)。目录组映射到角色。 - SSO配置,两个等效来源: 每个OIDC和LDAP(S)设置(启用标志、颁发者/服务器URL、客户端ID、作用域、绑定DN、基DN、用户/组过滤器、CA捆绑包、组到角色映射、自动配置)既可以从管理UI设置,也可以从环境变量(
BACKEND_OIDC_*、BACKEND_LDAP_*)设置。- 密钥(
client_secret、绑定密码)来自*_FILE变量或密钥提供者,生产中绝不使用明文环境变量值;在UI中它们是只写的(显示••••last4)。 - 优先级:由环境定义的设置获胜,并在管理UI中显示为只读,带有“由环境管理”徽章;其他所有内容均可编辑并以加密形式存储在数据库中。
- 两个来源使用相同的验证规则(由环境加载器和管理API共享的Pydantic模式);无效的环境配置会导致启动失败并显示清晰的错误消息,无效的UI输入在保存前被拒绝。
- 管理UI包含“测试连接”功能(OIDC发现 + JWKS获取,LDAP绑定 + 示例组搜索),每次更改都会记录审计日志。
- 每个…
- 密钥(
[翻译截断]