关于 aif-security-checklist
aif-security-checklist 是面向项目团队的安全审计技能,依据 OWASP Top 10 检查应用与基础设施风险。它支持按类别审计、跟踪忽略项,并输出机器可读的门禁结果。
主要功能
- 01基于 OWASP Top 10 的审计清单
- 02按安全类别进行专项检查
- 03自动化审计脚本
- 04在 SECURITY.md 跟踪忽略项
- 05附加机器可读的门禁结果
适用场景
- 01审查身份验证与会话处理
- 02检查代码中的注入和 XSS 风险
- 03评估 API 与基础设施安全
- 04记录待后续复核的接受风险
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lee-to/ ai-factory --skill aif-security-checklist安装后在 Claude Code 中输入/aif-security-checklist确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/AUTH-PATTERNS.mdPROMPT-INJECTION.mdRACE-CONDITIONS.mdscripts/audit.sh详细介绍
安全检查清单
基于 OWASP Top 10(2021)和行业最佳实践的全面安全检查清单。
快速参考
/aif-security-checklist— 完整审计检查清单/aif-security-checklist auth— 身份验证和会话/aif-security-checklist injection— SQL/NoSQL/命令注入/aif-security-checklist xss— 跨站脚本攻击/aif-security-checklist csrf— 跨站请求伪造/aif-security-checklist secrets— 密钥和凭据/aif-security-checklist api— API 安全/aif-security-checklist infra— 基础设施安全/aif-security-checklist prompt-injection— 大模型提示词注入/aif-security-checklist race-condition— 竞态条件和 TOCTOU/aif-security-checklist ignore <item>— 忽略特定检查项
配置
首先: 如果 .ai-factory/config.yaml 存在,请读取该文件以解析:
- 路径:
paths.security - 语言:
language.ui用于提示词、审计摘要和下一步指导;language.artifacts用于被忽略项状态产物;language.technical_terms用于被忽略项产物中面向用户的技术术语
如果 config.yaml 不存在,请使用默认值:
- SECURITY.md:
.ai-factory/SECURITY.md ui_language:enartifact_language:entechnical_terms_policy:keep
解析后的语言值:
ui_language = language.ui || "en"artifact_language = language.artifacts || language.ui || "en"technical_terms_policy = language.technical_terms || "keep"
如果 technical_terms_policy 不是 keep、translate 或 mixed 之一,则按 keep 处理。english 等旧值也按 keep 处理。
所有 AskUserQuestion 提示词、审计摘要、向用户显示的忽略项说明以及下一步指导都必须使用 ui_language 编写。
paths.security 指定的持久化 SECURITY.md 忽略项产物必须使用 artifact_language 编写。
模板和示例定义的是结构,而不是固定的英文输出。如果 artifact_language 不是 en,保存前请翻译面向用户的标题、表格标题、注释、生成的忽略项原因和审查指导。保留项目 ID、日期、作者用户名、命令、路径、配置键、包名称、API 名称、安全类别 ID、严重性/状态枚举值、原始错误,以及最终的 aif-gate-result JSON 架构,不做更改。对其他面向用户的术语应用 technical_terms_policy。
忽略项(SECURITY.md)
运行任何审计前,始终读取解析后的 SECURITY.md 路径(默认:.ai-factory/SECURITY.md)。如果文件存在,其中包含团队决定忽略的安全检查项。
忽略项的处理方式
当用户运行 /aif-security-checklist ignore <item> 时:
- 读取当前解析后的 SECURITY.md 文件(如果不存在则创建)
- 询问用户忽略此项的原因
- 按照下方格式将该项添加到文件中
- 确认已添加该项
运行任何审计(/aif-security-checklist 或特定类别)时:
- 在开始时读取解析后的 SECURITY.md 文件
- 对于与当前审计范围匹配的每个忽略项:
- 不要将其标记为发现项
- 而是在末尾的单独章节中显示:“⏭️ 忽略项”
- 显示每个忽略项及其原因和日期,以便团队了解情况
- 未被忽略的项目照常审计
SECURITY.md 格式
保存前,请先以 artifact_language 呈现此结构。下方标题是规范结构标签,并非固定的英文输出;条目 ID 和表格字段含义保持不变。
# Security: Ignored Items
Items below are excluded from security-checklist audits.
Review periodically — ignored risks may become relevant.
| Item | Reason | Date | Author |
|------|--------|------|--------|
| no-csrf | SPA with token auth, no cookies used | 2025-03-15 | @dev |
| no-rate-limit | Internal microservice, behind API gateway | 2025-03-15 | @dev |
条目命名约定 — 使用简短的 kebab-case ID:
no-csrf— 未实现 CSRF 令牌no-rate-limit— 未配置速率限制no-https— 未强制使用 HTTPSno-xss-csp— 缺少 CSP 标头no-sql-injection— 未完全防止 SQL 注入no-prompt-injection— 未缓解大模型提示词注入no-race-condition— 缺少竞态条件防护no-secret-rotation— 未轮换机密no-auth-{route}— 特定路由缺少身份验证verbose-errors— 暴露了详细错误信息- 或使用任何自定义描述性 ID
忽略条目的输出示例
显示审计结果时,请在末尾附上此部分:
⏭️ Ignored Items (from the resolved SECURITY.md artifact)
┌─────────────────┬──────────────────────────────────────┬────────────┐
│ Item │ Reason │ Date │
├─────────────────┼──────────────────────────────────────┼────────────┤
│ no-csrf │ SPA with token auth, no cookies used │ 2025-03-15 │
│ no-rate-limit │ Internal service, behind API gateway │ 2025-03-15 │
└─────────────────┴──────────────────────────────────────┴────────────┘
⚠️ 2 items ignored. Run `/aif-security-checklist` without ignores to see full audit.
项目上下文
阅读 .ai-factory/skill-context/aif-security-checklist/SKILL.md — 若该文件存在,则为强制要求。
此文件包含 /aif-evolve 根据补丁、代码库约定和技术栈分析积累的项目专属规则。这些规则专为当前项目定制。
如何应用 skill-context 规则:
- 将其视为对此技能通用指令的项目级覆盖规则
- 当 skill-context 规则与本 SKILL.md 中的通用规则冲突时,以 skill-context 规则为准(更具体的上下文优先级更高——遵循与嵌套 CLAUDE.md 文件相同的原则)
- 若没有冲突,则同时应用两者:SKILL.md 中的通用规则和 skill-context 中的项目规则
- 即使 skill-context 规则看似与此技能的默认规则矛盾,也不得忽略——这些规则源自项目实践,证明默认规则并不足够
- 重要: skill-context 规则适用于此技能的所有输出,包括安全检查清单、部署前检查清单和 SECURITY.md。如果 skill-context 规则要求“检查清单必须包含 X”或“审计必须涵盖 Y”,则必须相应补充检查清单。生成的安全报告若忽略 skill-context 规则,即为缺陷。
强制执行: 生成任何输出制品后,请根据所有 skill-context 规则进行核验。如果违反任何规则,必须先修正输出,再呈现给用户。
快速自动化审计
运行自动化安全审计脚本:
bash ~/{{skills_dir}}/security-checklist/scripts/audit.sh
此脚本会检查:
- 代码中的硬编码机密
- Git 中是否追踪了 .env
- .gitignore 配置
- npm audit(漏洞)
- 生产代码中的 console.log
- 安全任务标记
机器可读的门禁结果
对于 /aif-security-checklist 审计(完整审计或类别审计),请先输出供人阅读的安全报告,然后附上一个最终的 aif-gate-result fenced JSON 代码块。
除非该调用同时执行并报告审计结果,否则在 ignore <item> 写入流程中不要附加此门禁代码块。
状态映射:
fail:仍存在未忽略的严重/高危安全问题,或其他明确阻止生产部署的问题。warn:仅存在中/低危问题、需要复审的已忽略条目、审计证据不完整,或审计命令受限。pass:审计已完成,且不存在未忽略的问题。
机器可读字段:
- 使用
"gate": "security"。 - 使用
"status": "pass|warn|fail"。 - 使用
"blocking": true|false。 "blockers": [中仅包含阻止生产部署的问题。- 在
"affected_files": [中包含受影响的路径。 - 若适用工作流命令,则将
"suggested_next": {设为/aif-fix,否则设为null。 - JSON 代码块中不得包含机密、令牌、明文密码或私密凭据。
{
"schema_version": 1,
"gate": "security",
"status": "warn",
"blocking": false,
"blockers": [],
"affected_files": ["src/api/session.ts"],
"suggested_next": {
"command": "/aif-fix",
"reason": "Address non-blocking security hardening findings."
}
}
🔴 严重:部署前检查清单
上线前必须修复
- 代码或 git 历史中不得包含机密信息
- 验证并清理所有用户输入
- 所有受保护的路由均启用身份验证
- 强制使用 HTTPS(不使用 HTTP)
- 防止 SQL/NoSQL 注入
- 启用 XSS 防护
- 对会改变状态的请求使用 CSRF 令牌
- 启用速率限制
- 错误消息不得泄露敏感信息
- 在生产环境中禁用客户端调试日志,或通过明确的非生产环境检查进行保护
- 生产环境 UI 绝不显示原始错误、堆栈跟踪、异常消息、SQL 错误、请求内部信息或上游服务详情
- 扫描依赖项中的漏洞
- 缓解 LLM 提示词注入(如使用 AI)
- 防止关键操作(付款、库存)发生竞态条件
身份验证与会话
密码安全
✅ Requirements:
- [ ] Minimum 12 characters
- [ ] Hashed with bcrypt/argon2 (cost factor ≥ 12)
- [ ] Never stored in plain text
- [ ] Never logged
- [ ] Breach detection (HaveIBeenPwned API)
有关实现模式(argon2、bcrypt、PHP、Laravel)→ 请阅读 references/AUTH-PATTERNS.md
会话管理
✅ Checklist:
- [ ] Session ID regenerated after login
- [ ] Session timeout implemented (idle + absolute)
- [ ] Secure cookie flags set
- [ ] Session invalidation on logout
- [ ] Concurrent session limits (optional)
有关安全 Cookie 设置示例 → 请阅读 references/AUTH-PATTERNS.md
JWT 安全
✅ Checklist:
- [ ] Use RS256 or ES256 (not HS256 for distributed systems)
- [ ] Short expiration (15 min access, 7 day refresh)
- [ ] Validate all claims (iss, aud, exp, iat)
- [ ] Store refresh tokens securely (httpOnly cookie)
- [ ] Implement token revocation
- [ ] Never store sensitive data in payload
注入防护
SQL 注入
// ❌ VULNERABLE: String concatenation
const query = `SELECT * FROM users WHERE id = ${userId}`;
// ✅ SAFE: Parameterized query
const user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
// ✅ SAFE: ORM (Prisma/Eloquent/SQLAlchemy)
const user = await prisma.user.findUnique({ where: { id: userId } });
NoSQL 注入
// ❌ VULNERABLE: Direct user input — attack: { "$ne": "" }
const user = await db.users.findOne({ username: req.body.username });
// ✅ SAFE: Type validation
const username = z.string().parse(req.body.username);
命令注入
// ❌ VULNERABLE: exec(`convert ${userFilename} output.png`);
// ✅ SAFE: execFile('convert', [userFilename, 'output.png']);
跨站脚本攻击(XSS)
防护清单
- [ ] All user output HTML-encoded by default
- [ ] Content-Security-Policy header configured
- [ ] X-Content-Type-Options: nosniff
- [ ] Sanitize HTML if allowing rich text
- [ ] Validate URLs before rendering links
输出编码
// ❌ VULNERABLE: element.innerHTML = userInput; / dangerouslySetInnerHTML
// ✅ SAFE: element.textContent = userInput; / React: <div>{userInput}</div>
// ✅ If HTML needed: DOMPurify.sanitize(userInput)
// ❌ VULNERABLE: <?= $userInput ?> / {!! $userInput !!}
// ✅ SAFE: {{ $userInput }} (Blade) / htmlspecialchars($input, ENT_QUOTES, 'UTF-8')
内容安全策略
设置 CSP 标头:default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
CSRF 防护
清单
- [ ] CSRF tokens on all state-changing requests
- [ ] SameSite=Strict or Lax on cookies
- [ ] Verify Origin/Referer headers
- [ ] Don't use GET for state changes
实现
- 服务端渲染:使用
csurf中间件,在隐藏表单字段和 AJAX 标头中嵌入令牌 - 单页应用:使用双重提交 Cookie 模式——设置带有
sameSite: 'strict'的可读 Cookie,客户端在标头中发送令牌,服务端进行比对
机密信息管理
绝对不要这样做
❌ Secrets in code
const API_KEY = "sk_live_abc123";
❌ Secrets in git
.env committed to repository
❌ Secrets in logs
console.log(`Connecting with password: ${password}`);
❌ Secrets in error messages
throw new Error(`DB connection failed: ${connectionString}`);
清单
- [ ] Secrets in environment variables or vault
- [ ] .env in .gitignore
- [ ] Different secrets per environment
- [ ] Secrets rotated regularly
- [ ] Access to secrets audited
- [ ] No secrets in client-side code
清理 Git 历史
# If secrets were committed, remove from history
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch path/to/secret-file" \
--prune-empty --tag-name-filter cat -- --all
# Or use BFG Repo-Cleaner (faster)
bfg --delete-files .env
bfg --replace-text passwords.txt
# Force push (coordinate with team!)
git push origin --force --all
# Rotate ALL exposed secrets immediately!
API 安全
身份验证
- [ ] API keys not in URLs (use headers)
- [ ] Rate limiting per user/IP
- [ ] Request signing for sensitive operations
- [ ] OAuth 2.0 for third-party access
面向客户端的日志与错误
- [ ] Browser/client logs are disabled in production or routed through a logger that no-ops debug output in production
- [ ] `console.log`, `console.debug`, `console.info`, and verbose client telemetry are gated by explicit non-production checks
- [ ] Production UI shows only client-safe error messages with minimal operational detail
- [ ] Raw exceptions, stack traces, SQL/ORM errors, validation library internals, upstream responses, file paths, env names, and secrets never reach UI text
- [ ] Full error details are logged server-side only, correlated with a request/error ID returned to the client
- [ ] Client-safe error payloads use stable codes/messages such as `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `CONFLICT`, or `INTERNAL_ERROR`
const isProduction = process.env.NODE_ENV === 'production';
// ✅ Client debug output is explicit and removed/no-op in production paths
if (!isProduction) {
console.debug('Form validation state', formState);
}
// ✅ Normalize unknown errors before rendering them in UI
function toClientError(error: unknown) {
if (isKnownClientError(error)) {
return { code: error.code, message: error.publicMessage };
}
return {
code: 'INTERNAL_ERROR',
message: 'Something went wrong. Try again later.',
};
}
输入验证
// ✅ Validate all input with schema
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email().max(255),
name: z.string().min(1).max(100),
age: z.number().int().min(0).max(150).optional(),
});
app.post('/users', (req, res) => {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: {
code: 'VALIDATION_FAILED',
message: 'Some fields are invalid.',
fields: result.error.issues.map((issue) => ({
path: issue.path.join('.'),
code: issue.code,
})),
},
});
}
// result.data is typed and validated
});
响应安全
// ✅ Don't expose internal errors
app.use((err, req, res, next) => {
console.error(err); // Log full error internally
// Return generic message to client
res.status(500).json({
error: {
code: 'INTERNAL_ERROR',
message: 'Something went wrong. Try again later.',
},
requestId: req.id, // For support reference
});
});
// ✅ Don't expose sensitive fields
const userResponse = {
id: user.id,
name: user.name,
email: user.email,
// ❌ Never: password, passwordHash, internalId, etc.
};
基础设施安全
标头清单
app.use(helmet()); // Sets many security headers
// Or manually:
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '0'); // Disabled, use CSP instead
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
依赖项安全
# Check for vulnerabilities
npm audit
pip-audit
cargo audit
# Auto-fix where possible
npm audit fix
# Keep dependencies updated
npx npm-check-updates -u
部署清单
- [ ] HTTPS only (redirect HTTP)
- [ ] TLS 1.2+ only
- [ ] Security headers configured
- [ ] Debug mode disabled
- [ ] Default credentials changed
- [ ] Unnecessary ports closed
- [ ] File permissions restricted
- [ ] Logging enabled (but no secrets)
- [ ] Backups encrypted
- [ ] WAF/DDoS protection (for public APIs)
竞态条件
有关竞态条件的详细模式(重复扣款、TOCTOU、乐观锁、幂等键、分布式锁)→ 请阅读 references/RACE-CONDITIONS.md
防护清单
- [ ] Financial operations use database transactions with proper isolation
- [ ] Inventory/stock checks use atomic decrement (not read-then-write)
- [ ] Idempotency keys on payment and mutation endpoints
- [ ] Optimistic locking (version column) on concurrent updates
- [ ] File operations use exclusive locks where needed
- [ ] No TOCTOU gaps between permission check and action
- [ ] Rate limiting to reduce exploitation window
提示词注入(LLM 安全)
有关提示词注入的详细模式(直接注入、间接注入、工具安全、输出验证、RAG)→ 请阅读 references/PROMPT-INJECTION.md
防护清单
- [ ] User input never concatenated directly into system prompts
- [ ] Input/output boundaries clearly separated (delimiters, roles)
- [ ] LLM output treated as untrusted (never executed as code/commands)
- [ ] Tool calls from LLM validated and sandboxed
- [ ] Sensitive data excluded from LLM context
- [ ] Rate limiting on LLM endpoints
- [ ] Output filtered for PII/secrets leakage
- [ ] Logging & monitoring for anomalous prompts
快速审计命令
# Find hardcoded secrets
grep -rn "password\|secret\|api_key\|token" --include="*.ts" --include="*.js" .
# Check for vulnerable dependencies
npm audit --audit-level=high
# Find unfinished security markers
grep -rn "[T][O][D][O].*security\|[F][I][X][M][E].*security\|[X][X][X].*security" .
# Check for console.log in production code
grep -rn "console\.log" src/
# Check for verbose browser logs that need a non-production guard
grep -rn "console\.\(log\|debug\|info\|trace\)" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" src/
# Check for raw error rendering patterns in UI/client code
grep -rn "\(error\.message\|err\.message\|String(error)\|String(err)\|stack\)" --include="*.tsx" --include="*.jsx" --include="*.ts" --include="*.js" src/
# Find prompt injection risks (unsanitized input in LLM calls)
grep -rn "system.*\${.*}" --include="*.ts" --include="*.js" .
grep -rn "innerHTML.*llm\|innerHTML.*response\|innerHTML.*completion" --include="*.ts" --include="*.js" .
严重程度参考
| 问题 | 严重程度 | 修复时限 |
|---|---|---|
| SQL 注入 | 🔴 严重 | 立即 |
| 身份验证绕过 | 🔴 严重 | 立即 |
| 机密信息泄露 | 🔴 严重 | 立即 |
| XSS(存储型) | 🔴 严重 | < 24 小时 |
| 提示词注入(直接) | 🔴 严重 | 立即 |
| 竞态条件(财务) | 🔴 严重 | 立即 |
| 提示词注入(间接) | 🟠 高 | < 1 周 |
| 竞态条件(数据) | 🟠 高 | < 1 周 |
| CSRF | 🟠 高 | < 1 周 |
| XSS(反射型) | 🟠 高 | < 1 周 |
| 缺少速率限制 | 🟡 中 | < 2 周 |
| 错误信息过于详细 | 🟡 中 | < 2 周 |
| 缺少标头 | 🟢 低 | < 1 个月 |
提示: 安全审计后上下文内容较多。继续处理其他任务前,可考虑使用
/clear或/compact。
制品归属与配置策略
- 主要归属:通过
ignore流程创建的忽略项状态,由解析后的 SECURITY.md 制品(默认:.ai-factory/SECURITY.md)负责。 - 写入策略:审计发现通常以对话形式输出;除非用户明确要求,否则持久化写入仅限于上述忽略状态制品。
- 配置策略:支持配置感知。忽略状态制品使用
paths.security,提示词和审计摘要使用language.ui,忽略项制品使用language.artifacts,人类可读术语策略使用language.technical_terms;审计范围则根据仓库证据和审计命令确定。