关于 security-and-hardening
面向 Web 应用安全开发的技能,涵盖威胁建模、边界校验、安全加固和隐私保护,也涉及常见 Web 风险、AI/LLM 功能及依赖供应链。
主要功能
- 01围绕信任边界进行威胁建模
- 02输入校验与访问控制指导
- 03Web 和服务端风险加固
- 04隐私及个人数据处理实践
- 05AI/LLM 安全指导
- 06依赖与供应链审查
适用场景
- 01为 Web 功能规划安全控制
- 02审查身份验证和数据处理改动
- 03评估 LLM 功能的安全风险
- 04准备发布前的依赖变更
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ addyosmani/ agent-skills --skill security-and-hardening安装后在 Claude Code 中输入/security-and-hardening确认已加载。
附带文件references/hardening-patterns.md
详细介绍
安全与加固
概述
采用安全优先的 Web 应用开发实践。将每个外部输入视为恶意内容,将每个密钥视为至关重要的信息,并将每次授权检查视为必需步骤。安全不是一个阶段,而是对每一行涉及用户数据、身份验证或外部系统的代码的约束。
适用场景
- 构建任何接受用户输入的内容
- 实现身份验证或授权
- 存储或传输敏感数据
- 集成外部 API 或服务
- 添加文件上传、Webhook 或回调
- 处理支付或 PII 数据
流程:先进行威胁建模
没有威胁模型就硬加的控制措施,只能算猜测。加固之前,花五分钟换位思考,像攻击者一样思考:
- 梳理信任边界。 不可信数据会在哪里进入系统?HTTP 请求、表单字段、文件上传、Webhook、第三方 API、消息队列和 LLM 输出——还有一些看起来像内部数据、只是因为 OS 把它们交给你的本地值:另一个进程的命令行或环境变量、共享卷上的文件名、作业负载中的路径。信任取决于值的编写者,而非传递渠道。每个边界都是攻击面。
- 列出资产。 什么东西值得窃取或破坏?凭据、PII、支付数据、管理员操作、资金转移。
- 对每个边界进行 STRIDE 分析——这是一种快速审视方法,不是形式主义:
| 威胁 | 要问的问题 | 常见缓解措施 |
|---|---|---|
| S 欺骗 | 有人能冒充用户/服务吗? | 身份验证、签名验证 |
| T 篡改 | 数据在传输中或静态存储时能被修改吗? | 完整性检查、参数化查询、HTTPS |
| R 抵赖 | 事后能否否认某项操作? | 记录安全事件的审计日志 |
| I 信息泄露 | 数据会泄露吗? | 加密、字段允许列表、通用错误信息 |
| D 拒绝服务 | 系统可能因不堪重负而瘫痪吗? | 速率限制、输入大小上限、超时 |
| E 权限提升 | 用户能获得不该拥有的权限吗? | 授权检查、最小权限 |
- 在使用场景旁边编写滥用场景。 对每项功能,都问一句“我会如何滥用它?”——然后把它作为第一个测试。
如果说不清某项功能的信任边界,就还没准备好保护它。这就是 OWASP A04:不安全设计——大多数安全漏洞始于设计,而非代码。
三级边界系统
始终执行(无例外)
- 在系统边界(API 路由、表单处理程序)验证所有外部输入
- 对所有数据库查询进行参数化处理——绝不将用户输入拼接到 SQL 中
- 对输出进行编码以防止 XSS(使用框架的自动转义,不要绕过它)
- 所有外部通信都使用 HTTPS
- 使用 bcrypt/scrypt/argon2 对密码进行哈希处理(绝不存储明文密码)
- 设置安全标头(CSP、HSTS、X-Frame-Options、X-Content-Type-Options)
- 会话使用 httpOnly、secure、sameSite Cookie
- 每次发布前,使用检测到的包管理器原生审计功能审计已提交的锁文件
先征求意见(需要人工批准)
- 添加新的身份验证流程或更改身份验证逻辑
- 存储新的敏感数据类别(PII、支付信息)
- 添加新的外部服务集成
- 更改 CORS 配置
- 添加文件上传处理程序
- 修改速率限制或节流设置
- 授予更高权限或角色
绝不执行
- 绝不将密钥提交到版本控制中(API 密钥、密码、令牌)
- 绝不记录敏感数据(密码、令牌、完整信用卡号)
- 绝不将客户端验证视为安全边界
- 绝不为了方便而禁用安全标头
- 绝不对用户提供的数据使用
eval()或innerHTML - 绝不将会话存储在客户端可访问的存储中(例如使用 localStorage 存储身份验证令牌)
- 绝不向用户暴露堆栈跟踪或内部错误详情
加固控制
以下规则构成工作流;每项规则的具体实现见 references/hardening-patterns.md。用到相关代码时再打开所需章节,无需提前查看。
注入、XSS 和访问控制
- 对每个查询使用参数化方式。绝不根据输入字符串构造 SQL、NoSQL 或 shell 命令。
- 通过框架的自动转义功能对输出进行编码。如果无法避免使用原始 HTML,须先通过允许列表清理器进行清理。
- 每个请求都要检查授权,而不仅是身份验证:经过身份验证的用户必须拥有特定资源,或获准访问该资源(A01,IDOR)。
身份验证和会话
- 使用 bcrypt(≥12 轮)、scrypt 或 argon2 对密码进行哈希处理。会话密钥必须来自环境变量,不能写在代码中。
- 会话 Cookie 必须设置
httpOnly、secure和sameSite: 'lax'或'strict'(用于防御 CSRF;'none'会在跨站请求中发送 Cookie),并设置有界的maxAge。
模式:身份验证。
标头、CORS 和响应
- 每个响应都要包含安全标头(使用 helmet 或框架的等效功能);CSP 从
default-src 'self'开始,并逐步收紧,而不是放宽。 - CORS 仅允许配置中明确列出的来源。启用凭据时,绝不能使用
*。 - 在发送任何响应前,移除敏感字段(
passwordHash、重置令牌)。错误响应正文应使用通用内容;内部信息只能写入服务端日志。
输入验证和上传
- 在边界处使用 schema 进行验证:允许列表中的结构、长度、枚举值和格式。拒绝时返回 422 和结构化详情;下游代码只能使用解析后的类型化值。
- 上传:使用允许列表限制 MIME 类型、限制大小,并在必要时验证内容(魔数)。文件扩展名不能证明任何内容。
服务端抓取(SSRF)
用户能够影响的任何 URL——webhook、从 URL 导入、图片代理、链接预览——都可能被指向内部服务。使用允许列表限制 scheme 和 host,解析所有 DNS 记录并拒绝任何私有或保留地址(回环、链路本地 169.254.169.254、私有地址、IPv4 和 IPv6 的唯一本地地址),并禁止重定向。此检查仍存在 DNS 重绑定 TOCTOU 漏洞:对于高风险入口,应固定已解析的 IP,或在前面设置过滤 Agent。
模式:SSRF。
对派生路径执行破坏性操作
删除、移动或覆盖操作的安全性取决于用于指定目标的值;信任应取决于该值的写入者,而不是传递它的渠道:另一个进程的命令行与表单字段一样,都可能受攻击者控制。形状检查只能证明格式正确,不能证明已获授权。调用前必须同时满足以下三项:解析后的目标(已解析符号链接)位于允许列表中的根目录之下;目标至少位于该根目录下一层;并且目标具有在操作前读取的所有权证据。拒绝时,记录被拒绝的目标并停止;绝不能回退到范围更广的默认路径。
为什么这项检查并没有看起来那么可靠(标记自我证明、检查与使用之间的竞争条件):破坏性路径。示例代码:../../references/security-checklist.md。
速率限制
对 API 实施常规限流,并严格限制身份验证端点(每 15 分钟约 10 次尝试)。一旦多个进程开始处理流量,内存计数器就会悄然变成 max × instances,或者在无服务器环境中完全不触发:请使用共享存储作为限流器的后端。
模式:速率限制。
密钥
密钥来自环境变量。提交 .env.example 并使用占位符;将真实的 .env* 文件和密钥材料加入 gitignore;提交前检查暂存的差异。密钥一旦到达远程端,就已泄露:先轮换密钥,再清除历史记录。
模式:密钥管理。
依赖项和供应链
- 找到安装边界和包管理器。 使用拥有锁文件的工作区根目录;只有在独立的嵌套项目位于该工作区之外时,才使用该项目。交叉核对
packageManager(如有)、锁文件和 CI;如果信息不一致或存在多个竞争的锁文件,请停止操作。固定包管理器版本。 - 首次执行前阻止依赖项脚本。 禁用脚本或采用有文档记录的默认拒绝策略进行引导;检查待执行脚本的源代码,只批准最低限度的脚本,提交策略,然后通过干净的冻结/不可变安装进行验证。切勿一概批准。
- 每次发布前,使用已提交的锁文件运行原生审计。 根据可达性(运行时、构建、测试、部署路径)确定严重/高危问题的优先级,并修复影响可用性的问题。切勿自动执行强制修复(
npm audit fix --force或同类操作),因为强制修复可能超出已声明的依赖范围;请先预览、阅读变更日志,并逐项测试升级。每项延期都要记录理由和复审日期。 - 审计只能匹配已知公告。 它们无法发现新出现的恶意包或仿冒包(
cross-env与crossenv)。请一并审查新增依赖项、锁文件差异和脚本策略变更:所有权、维护情况、发布年限、来源证明和传递依赖图。支持的情况下验证注册表签名(npm audit signatures、pnpm audit signatures);如果缺少签名,应将其视为需要调查的信号,而非自动认定已遭入侵(A06、LLM03)。
确定处理优先级的决策树:依赖项审计分流。包管理器矩阵和安装脚本门控:../../references/security-checklist.md。
个人数据与隐私
安全加固关注“攻击者能否读取它?”,隐私关注“我们是否应该持有它?要持有多久?”最容易保护、应对泄露和满足合规要求的数据,就是从未收集的数据;请将个人数据视为需要尽量减少的责任。
- 添加字段时对其进行分类(非个人数据、PII、敏感数据),并据此处理各类数据。找不到数据,就无法保护它,也无法响应删除请求。
- 仅为明确说明的目的收集数据。“以后可能有用”是潜在的数据泄露范围,不是收集目的。不要在遥测数据中包含 PII(
observability-and-instrumentation技能也从运维角度强调了这一点)。 - 预先设定保留期限,并真正删除数据。 每个个人数据存储都需要 TTL 和可用的删除路径,包括备份、缓存、搜索索引和分析副本。
- 支持司法管辖区要求的数据主体权利(GDPR、CCPA 等):导出、更正、删除。设计架构时,应确保能够找到并删除用户数据,而不是让数据不可逆地散布到各个系统中。
- 收集数据和向第三方共享数据均须经过同意,并且同意过程可供审计。 将 PII 发送给分析、广告或大模型供应商即构成共享;供应商需要签署数据处理协议。将地区设为可配置的策略,而非硬编码的假设。
分类表:数据分类。隐私事件会启动数据泄露通知时限;请使用 debugging-and-error-recovery 技能进行事后复盘。
AI / LLM 功能
调用大模型——聊天机器人、摘要工具、Agent、RAG——会带来新的攻击面;请参照 OWASP 大模型应用十大风险(2025)梳理这些风险:
- 模型输出是不可信输入(LLM05)。绝不能将其传入
eval、SQL、shell、innerHTML或文件路径;应谨慎解析,根据架构验证,然后编码。 - 提示词可能被劫持(LLM01)。上下文中的不可信文本——用户消息、抓取的页面、PDF——都可能包含指令。系统提示词不是安全边界;请在代码中强制执行权限。
- 不要将机密、其他租户的数据和完整系统提示词放入上下文窗口(LLM02、LLM07);限定工具权限,验证每个工具参数,并确认破坏性操作(LLM06);限制 token 数量、请求速率和递归深度(LLM10);按租户划分 RAG 嵌入,并在索引前验证文档(LLM08)。
模式:LLM 输出处理。
审查清单
签核前,请逐项检查 ../../references/security-checklist.md:其中涵盖身份验证、授权、输入、数据保护与隐私、标头与 CORS、依赖项与供应链、AI/LLM 和错误处理,以及 OWASP 快速参考表。
常见借口
| 借口 | 实际情况 |
|---|---|
| “这是内部工具,安全不重要” | 内部工具也会遭到入侵。攻击者会瞄准最薄弱的一环。 |
| “以后再加安全措施” | 事后补上安全措施要比一开始就纳入设计难 10 倍。现在就加上。 |
| “没人会试图利用这个” | 自动化扫描器会找到它。依赖隐蔽性并不等于安全。 |
| “框架会处理安全问题” | 框架提供的是工具,而非保障。你仍然需要正确使用这些工具。 |
| “这只是个原型” | 原型会进入生产环境。从第一天起就养成安全习惯。 |
| “在这里做威胁建模太过了” | 花五分钟想想“我会如何攻击它?”,就能避免那些事后无法靠任何控制措施修补的设计缺陷。 |
| “这只是大模型的输出,只是文本” | 这种“文本”可能是 SQL 语句、脚本标签或 shell 命令。应将其视为任何其他不可信输入。 |
| “审计通过了,所以依赖项是安全的” | 审计针对的是已知公告。它无法发现新出现的恶意软件包,也无法确保未经审查的安装脚本可安全执行。 |
| “先收集起来,以后可能用得上” | 不持有的数据就不会遭遇泄露、传票或误删。“可能用得上”属于泄露范围,而非收集目的。 |
| “我们会手动处理删除请求” | 手动删除会漏掉备份、缓存和分析数据副本。如果架构无法找到用户的数据,就无法满足其请求——请从设计上考虑这一点。 |
| “合规是法务的事,不是我们的事” | 导出、删除、保留和同意都是架构与代码的一部分。要是你已将个人身份信息散落在十个系统中,法务也无法事后补上这些功能。 |
红旗问题
- 用户输入直接传入数据库查询、Shell 命令或 HTML 渲染
- 删除、移动或覆盖操作的目标来自载荷、配置值或其他进程的命令行,且仅通过路径格式检查加以防护
- 源代码或提交历史中包含密钥
- API 端点没有身份验证或授权检查
- 缺少 CORS 配置,或允许通配符(
*)来源 - 身份验证端点没有速率限制,或在多个实例前使用内存中的限流器
- 向用户暴露堆栈跟踪或内部错误
- 依赖项存在已知严重漏洞、同一安装边界存在相互竞争的锁文件、安装过程不可复现,或脚本一律获准运行
- 服务端获取用户提供的 URL,且没有使用允许列表(SSRF)
- 将 LLM/模型输出传入查询、DOM、Shell 或
eval - 将密钥、PII 或完整系统提示词放入 LLM 上下文窗口
- 收集个人数据,却未说明用途、保留期限或删除途径
- 未经同意或未签订数据处理协议,就将 PII 发送给分析、广告或 LLM 服务商
- “删除我的账户”只切换了一个标记,个人数据却仍留在存储系统和备份中
验证
实施与安全相关的代码后:
- 原生审计不存在未缓解且可触达的严重/高危问题;CI 保留权威锁文件,并阻止未经审核的依赖脚本
- 源代码或 git 历史中没有密钥
- 在系统边界验证所有用户输入
- 在执行破坏性文件系统操作前,先解析符号链接,再验证允许的根路径、最小深度和所有权
- 在每个受保护的端点检查身份验证和授权
- 响应中包含安全标头(使用浏览器 DevTools 检查)
- 错误响应不会暴露内部详情
- 身份验证端点已启用速率限制;当多个实例提供流量服务时,使用共享存储支持限流
- 根据允许列表验证服务端 URL 获取操作(避免 SSRF)
- 使用前验证并编码 LLM/模型输出(如存在 AI 功能)
- 对个人数据进行分类,按明确用途尽量减少收集,并设定保留期限
- 删除和导出请求端到端可用(包括备份、缓存和分析副本)