mcpField
A
api-security-best-practices

api-security-best-practices

github.com/sickn33安全48.7k8.9k 次安装今天更新
代码仓库

关于 api-security-best-practices

面向 API 开发与审查的安全实践指南,覆盖身份验证、资源授权、输入校验、滥用控制和日志。强调按实际应用的身份与数据模型验证边界,并明确示例和检查不等同于已完成的审计。

主要功能

  1. 01验证令牌签名及声明
  2. 02按用户和租户授权资源
  3. 03校验输入并限制请求体
  4. 04设计速率限制与滥用控制
  5. 05安全处理密码、密钥和日志

适用场景

  1. 01审查受保护的 API 端点
  2. 02排查跨用户或跨租户访问
  3. 03加固令牌刷新流程
  4. 04验证输入和限流失败场景

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sickn33/agentic-awesome-skills --skill api-security-best-practices

安装后在 Claude Code 中输入/api-security-best-practices确认已加载。

详细介绍

API 安全最佳实践

从调用方身份到授权、经过验证的输入、存储和可观测响应,全面审视请求边界。保留应用实际的 身份提供方和数据模型,而不是引入第二套认证系统。

何时使用

在添加受保护端点、审查对象访问、替换宽松的请求解析或调查 API 滥用路径时使用。对于具体的 缺陷,从出问题的路由及其调用方入手;不要部署无关的安全基础设施。

输入与前置条件

记录路由、调用方/租户模型、身份提供方、令牌契约、运行时和已锁定的依赖版本、数据库 schema、代理拓扑以及已授权的测试范围。在测试环境中使用合成身份。现有任务授权继续沿用; 生产环境扫描、账户写入和消息发送需要各自独立的授权。下面的 Node 示例是 Express、 jsonwebtoken 和 Zod 的集成草图;应用/数据库适配器是有意重命名的,并非作为完整可运行的 服务呈现。集成前请根据已安装版本确认 API。

1. 验证确切的令牌契约

优先使用既有的提供方/会话中间件。当服务自行持有 HMAC JWT 契约时,要求使用强大的服务端 自有密钥、固定算法、确切的 issuer 和 audience,以及必需的运行时声明。在签名验证之前,不要 从已解码的令牌推断权限。绝不接受调用方自行选择的验证算法。

const jwt = require('jsonwebtoken');

// Illustrative first-party access-token contract; not a third-party OAuth adapter.
const ACCESS_POLICY = {
  algorithms: ['HS256'], issuer: 'example-auth', audience: 'example-api'
};
function verifyAccessToken(token, signingKey) {
  const claims = jwt.verify(token, signingKey, ACCESS_POLICY);
  if (!claims || typeof claims !== 'object' ||
      typeof claims.sub !== 'string' || !claims.sub ||
      typeof claims.tenantId !== 'string' || !claims.tenantId ||
      !Number.isSafeInteger(claims.exp) || !Number.isSafeInteger(claims.iat) ||
      claims.exp <= claims.iat) {
    throw new Error('Invalid access claims');
  }
  return { subject: claims.sub, tenantId: claims.tenantId };
}
function readBearer(header) {
  if (typeof header !== 'string' || header.length > 8192) return null;
  const match = /^Bearer ([A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)$/i.exec(header);
  return match ? match[1] : null;
}

签发访问令牌时使用相同的 issuer/audience/algorithm 以及一个应用批准的较短有效期。将验证 失败作为通用的 401 处理,不回显令牌或异常。仅凭过期并不能撤销令牌;根据实际威胁模型定义 撤销机制或短生命周期会话。使用非对称提供方密钥的服务需要提供方的 discovery/JWKS 验证和 密钥轮换策略,而不是这个 HMAC 示例。绝不要将访问令牌复用为刷新令牌。

刷新会话

使用提供方支持的会话流程或服务端不透明刷新设计:仅存储摘要、过期时间、用户/会话族和撤销 状态。在一个原子事务中消费旧的活跃令牌并创建替换令牌。并发复用不得签发两个后继令牌; 按已定义的复用处理方式撤销受影响的会话族。签发新的访问令牌时检查当前用户状态和权限。将 刷新绑定到预期的客户端/会话,并保护基于 cookie 的请求免受 CSRF 攻击。不要记录令牌、不要 将其以明文存储在数据库中,也不要通过 URL 返回刷新令牌。在宣布流程完成之前,测试同时刷新、 过期、重放、撤销和事务失败。此处未捆绑数据库事务适配器。

2. 授权资源与操作

认证识别调用方;授权决定针对某个对象和租户的确切操作。角色名称不会自动授予跨租户访问。 在数据库变更中应用所有者/租户谓词,以避免检查后写入的竞态,并将可写属性加入允许列表。 根据产品的资源披露策略一致地使用 404/403。

// Prisma-style sketch; id and tenant types must match your actual schema.
async function deleteOwnedPost(prisma, postId, principal) {
  const result = await prisma.post.deleteMany({
    where: { id: postId, userId: principal.subject, tenantId: principal.tenantId }
  });
  return result.count === 1;
}

管理员路径需要显式独立的策略和审计事件;不要为每个所有者检查添加隐式的管理员绕过。测试 有效用户访问另一用户的对象、另一租户中的相同 ID、已删除的成员关系以及批量端点。

3. 只解析一次,然后使用经过校验的值

拒绝部分数字解析(12abc 不是 ID 12)、不安全的整数、意外属性和超大请求。使用参数化数据库查询。ORM 并不提供业务授权,也不能让不安全的原始 SQL 变得安全。

function parsePositiveId(raw) {
  if (typeof raw !== 'string' || !/^[1-9][0-9]{0,15}$/.test(raw)) return null;
  const value = Number(raw);
  return Number.isSafeInteger(value) && value > 0 ? value : null;
}

const { z } = require('zod');
const profileUpdate = z.object({
  displayName: z.string().trim().min(1).max(100)
}).strict();
function validateBody(schema) {
  return (req, res, next) => {
    const parsed = schema.safeParse(req.body);
    if (!parsed.success) {
      return res.status(400).json({ error: 'Invalid request' });
    }
    req.validatedBody = parsed.data; // Defaults/transforms must reach the handler.
    next();
  };
}
// Handler uses req.validatedBody, never the original body or an arbitrary spread.

在解析之前设置请求体大小限制。Zod 形状校验只是其中一层:在事务中检查当前归属、允许的状态转换和唯一性。对于 HTML,只允许使用经过维护的净化器所支持的必需标签/属性,然后用目标环境的安全输出 API 进行渲染。对于纯文本评论,优先使用纯文本;对字符串进行净化并不能使其在每种 JavaScript、URL 或 HTML 上下文中都安全。也要将上游 API 的响应视为不可信输入进行校验。

对于出站 URL,要定义允许的协议/主机、重定向行为、解析后的 IP 范围、凭据策略、超时和响应大小。仅靠正则表达式或 URL 解析器无法防止 SSRF 或 DNS 重绑定。文件上传同样需要类型/内容检查、大小限制、隔离存储以及读取时的授权。

4. 在不宣称 DDoS 防护的前提下控制滥用

使用现有的网关和经过维护的限流存储。在推导已认证用户键之前先进行身份认证,绝不要信任用户提供的层级。对于匿名流量,使用库所支持的、能识别 IPv6 的 IP 键处理方式,并将 Express 的 trust proxy 配置为实际可信的跳数;不要盲目为所有调用方启用它。

对于分布式配额,使用带过期时间的原子计数器实现,并定义存储故障时的行为。避免手写 INCR 后跟 EXPIRE:两者之间发生崩溃可能会留下永久键。区分每用户配额、每 IP 滥用控制、并发限制和上游服务预算。记录实际的窗口/重置语义,并发送准确的 Retry-After,而不是硬编码的整个窗口值。除非配置了共享存储,否则内存限流器是每进程独立的。

测试并发请求、IPv4/IPv6、伪造的转发头、未知层级、身份缺失、Redis 故障和过期。应用层限流无法吸收网络饱和。Helmet 配置的是 HTTP 响应头;它不是 DDoS 防护、访问控制,也不能替代上游容量控制。针对实际部署和子域策略推出 CSP/HSTS;不要盲目复制预加载设置。

5. 密码、密钥和日志

尽可能使用既有的身份提供商。对于存储的密码,使用经过维护的密码哈希方案并校准参数(新设计优先使用 Argon2id)。检查已泄露/常见密码并支持口令短语。不要强加任意的的大写/符号组合规则,也不要静默截断长密码。旧版 bcrypt 有输入字节上限,迁移时必须明确考虑;密码的字符长度和 UTF-8 字节数是不同的。

将密钥保存在经批准的密钥机制中,在启动时检查必需配置而不打印其值,并轮换已暴露的凭据。绝不要在常规日志中包含原始令牌、密码、请求体或完整的数据库异常。在适当的保留/访问策略下,记录有界的事件名称、请求关联以及安全的状态/错误类别。经过净化的错误不应返回可批量赋值的用户对象。CORS 控制的是浏览器跨源访问;它不是 API 认证或 CSRF 防护。

完整示例:资料更新边界

给定 PATCH /users/:id,使用字符串 ID 和可编辑的显示名称:

  1. 在 fixtures 中记录已授权的调用方/租户以及当前端点行为。
  2. 检查缺失/过期/错误受众的 token 是否在访问存储之前返回 401。
  3. 发送 12abc、一个不安全的整数、空名称以及额外的 role 字段;预期返回 400 且不发生数据库变更。发送一个带填充的有效名称;确认只有解析后去除填充的 值才会到达按所有者与租户限定范围的更新。
  4. 尝试其他用户的有效 ID 以及跨租户 ID;预期得到文档所述的拒绝 且不发生变更。有效的所有者请求仅更新允许的属性。
  5. 触发配额/存储失败,并确认日志中不包含请求 token 或名称。

返回路由策略、复现的失败用例、确切的测试命令/输出以及 剩余缺口。这些是应在目标应用中执行的预期检查,并非声称本仓库已测试过 已部署的 API。

限制

本指南不是完整的身份服务、认证安全审计或渗透 测试。代码片段省略了应用适配器和集成错误中间件。解析器的单元测试 并不验证数据库事务、代理行为或提供商会话。 请明确报告任何未测试的路由、租户路径和失败模式。不要根据通过的结构性检查 或风险元数据标签推断出安全状况良好。

参考资料

评论

支持 Markdown