名称: api-security 描述: 用于实现API认证、授权或安全模式时使用。涵盖OAuth 2.0、OIDC、JWT、API密钥、速率限制和常见API安全漏洞。 允许工具: Read, Glob, Grep
API安全
API安全的全面指南 - 认证、授权和常见漏洞防护。
何时使用此技能
- 实现API认证(OAuth、OIDC、JWT)
- 设计API授权模型
- 保护API端点
- 理解API安全漏洞
- 实现速率限制和滥用防护
- API密钥管理
认证模式
OAuth 2.0流程
OAuth 2.0授权类型:
1. 授权码(带PKCE)
└── 最适合:Web应用、移动应用、SPA
└── 用户认证最安全
用户 ──► 认证服务器 ──► 授权码 ──► 令牌
2. 客户端凭证
└── 最适合:服务到服务(M2M)
└── 无用户上下文,服务器到服务器
服务 ──► 认证服务器 ──► 访问令牌
3. 设备授权(设备流程)
└── 最适合:智能电视、物联网、输入受限设备
└── 用户在单独设备上授权
设备 ──► 显示代码 ──► 用户在手机上输入 ──► 令牌
已弃用(避免使用):
- 隐式流程(安全问题)
- 资源所有者密码凭证(反模式)
授权码流程带PKCE
┌──────────┐ ┌───────────────┐
│ 客户端 │ │ 认证服务器 │
└────┬─────┘ └───────┬───────┘
│ │
│ 1. 生成code_verifier(随机) │
│ 2. 计算code_challenge = SHA256(verifier)
│ │
│──3. 授权请求 ───────────────►│
│ (client_id, redirect_uri, │
│ code_challenge, challenge_method) │
│ │
│◄──4. 授权码 ──────────────────│
│ (用户认证/同意后) │
│ │
│──5. 令牌请求 ───────────────────────►│
│ (code, code_verifier) │
│ │
│◄──6. 访问令牌 + 刷新令牌 ────────│
│ │
为什么使用PKCE?
- 防止授权码拦截攻击
- 移动应用和SPA必需
- 推荐用于所有OAuth流程
OpenID Connect (OIDC)
OIDC = OAuth 2.0 + 身份层
OIDC添加:
├── ID令牌(带用户声明的JWT)
├── UserInfo端点
├── 标准化声明(sub, name, email等)
└── 发现和元数据
令牌类型:
┌─────────────────────────────────────────────────┐
│ ID令牌 │
│ - 用户身份(他们是谁) │
│ - 包含认证相关的声明 │
│ - 供客户端消费 │
├─────────────────────────────────────────────────┤
│ 访问令牌 │
│ - 授权(他们能做什么) │
│ - 供API/资源服务器验证 │
│ - 不应由客户端解析 │
├─────────────────────────────────────────────────┤
│ 刷新令牌 │
│ - 长期凭证 │
│ - 用于获取新访问令牌 │
│ - 安全存储,绝不暴露到浏览器 │
└─────────────────────────────────────────────────┘
JWT(JSON Web令牌)
JWT结构:
头部.载荷.签名
头部(Base64URL):
{
"alg": "RS256", // 算法
"typ": "JWT", // 类型
"kid": "key-123" // 密钥ID用于轮换
}
载荷(Base64URL):
{
"iss": "https://auth.example.com", // 颁发者
"sub": "user-12345", // 主体
"aud": "https://api.example.com", // 受众
"exp": 1735689600, // 过期时间
"iat": 1735686000, // 颁发时间
"scope": "read write" // 范围/权限
}
签名:
RSASHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
私钥
)
JWT验证
JWT验证清单:
1. 签名验证
□ 使用正确公钥验证(来自JWKS)
□ 如果签名无效则拒绝
2. 声明验证
□ iss(颁发者):匹配预期颁发者
□ aud(受众):包含你的API标识符
□ exp(过期时间):令牌未过期
□ iat(颁发时间):未在未来颁发
□ nbf(不早于):令牌已激活
3. 额外检查
□ 令牌不在撤销列表中(如适用)
□ 必需范围存在
□ 令牌类型是access_token(非id_token)
常见错误:
❌ 信任"alg": "none"
❌ 未验证受众
❌ 对公共API使用对称密钥
❌ 在JWT载荷中存储敏感数据
授权模式
基于角色的访问控制(RBAC)
RBAC模型:
用户 ──► 角色 ──► 权限
示例:
┌──────────────────────────────────────┐
│ 角色:编辑器 │
├──────────────────────────────────────┤
│ 权限: │
│ - articles:read │
│ - articles:create │
│ - articles:update │
│ - comments:moderate │
└──────────────────────────────────────┘
API实现:
GET /articles ← 需要:articles:read
POST /articles ← 需要:articles:create
PUT /articles/{id} ← 需要:articles:update
DELETE /articles/{id} ← 需要:articles:delete
令牌包含:
{
"scope": "articles:read articles:create articles:update"
}
基于属性的访问控制(ABAC)
ABAC模型:
访问 = f(主体, 资源, 操作, 环境)
主体属性:
- 角色、部门、安全级别
- 用户ID、组成员资格
资源属性:
- 所有者、分类、类型
- 创建日期、敏感性
操作属性:
- 读、写、删除、批准
环境属性:
- 时间、IP地址、设备类型
示例策略:
"允许如果 user.department == resource.department
AND user.role == 'manager'
AND action == 'approve'
AND environment.time.isBusinessHours"
基于资源的授权
在资源级别检查所有权/访问:
GET /documents/{id}
授权检查:
1. 验证令牌(认证)
2. 从令牌提取用户
3. 查询:此用户可以访问此文档吗?
- 用户是所有者吗?
- 用户有显式权限吗?
- 文档在用户可访问的共享文件夹中吗?
- 用户在有权访问的组中吗?
4. 如果未授权返回403
实现模式:
async function authorize(userId, resourceId, action) {
// 检查直接所有权
if (await isOwner(userId, resourceId)) return true;
// 检查显式权限
if (await hasPermission(userId, resourceId, action)) return true;
// 检查组权限
if (await hasGroupPermission(userId, resourceId, action)) return true;
return false;
}
API密钥安全
API密钥最佳实践
API密钥设计:
格式:前缀_随机字节
示例:sk_live_a1b2c3d4e5f6...
前缀好处:
- sk_ = 秘密密钥(仅服务器端)
- pk_ = 公共密钥(可暴露)
- test_ = 测试环境
- live_ = 生产环境
安全要求:
□ 足够熵(32+字节随机)
□ 安全存储(哈希化,非明文)
□ 范围化权限(非全有或全无)
□ 轮换能力
□ 审计日志
□ 每密钥速率限制
API密钥 vs OAuth
何时使用各自:
API密钥:
✓ 服务到服务集成
✓ 简单用例
✓ 面向开发者的API
✓ 无状态、简单认证
✗ 不适合用户上下文
✗ 难以实时撤销
OAuth 2.0:
✓ 用户委派访问
✓ 细粒度范围
✓ 短期令牌
✓ 撤销支持
✓ 标准合规
✗ 实现更复杂
✗ 需要认证服务器
常见漏洞
OWASP API安全前10
1. 对象级别授权破坏(BOLA)
问题:通过更改ID访问其他用户资源
修复:始终验证用户对特定资源的访问权限
❌ GET /users/123/orders(任何用户可更改123)
✓ 验证请求用户能访问用户123的订单
2. 认证破坏
问题:弱认证机制
修复:使用成熟认证协议,正确实现
❌ 自定义令牌方案、弱密码
✓ OAuth 2.0/OIDC带MFA
3. 对象属性级别授权破坏
问题:暴露敏感属性
修复:基于授权过滤响应
❌ 在API响应中返回user.password_hash
✓ 显式选择返回字段
4. 无限制资源消耗
问题:对请求/数据无限制
修复:速率限制、分页、资源配额
❌ GET /users返回1000万条记录
✓ 分页、查询限制、速率限制
5. 功能级别授权破坏
问题:端点缺少授权
修复:验证每个功能的授权
❌ 管理端点对普通用户可访问
✓ 所有端点进行角色检查
6. 对敏感业务流程无限制访问
问题:业务逻辑滥用
修复:检测和限制滥用模式
❌ 无限制密码重置尝试
✓ 敏感流程速率限制 + CAPTCHA
7. 服务器端请求伪造(SSRF)
问题:API获取攻击者控制URL
修复:验证和清理URL,允许列表
❌ POST /fetch { "url": "http://internal-service" }
✓ 根据允许列表验证,阻止内部IP
8. 安全配置错误
问题:默认配置、暴露错误
修复:强化配置、最小化错误
❌ 生产环境中详细堆栈跟踪
✓ 通用错误消息、正确头信息
9. 不当库存管理
问题:未跟踪API版本、影子API
修复:API库存、版本管理
❌ 旧API版本仍可访问
✓ 库存、生命周期管理、弃用
10. API不安全消费
问题:信任第三方API响应
修复:验证所有外部数据
❌ 直接使用第三方响应数据
✓ 验证、清理、视为不可信
输入验证
验证层:
1. 语法验证
- 是有效JSON/XML吗?
- 必需字段存在吗?
- 字段类型正确吗?
2. 语义验证
- 邮箱格式有效吗?
- 日期在有效范围吗?
- ID引用存在吗?
3. 业务规则验证
- 数量在限制内吗?
- 用户对此操作授权吗?
- 状态转换合理吗?
验证模式:
- 允许列表优于阻止列表
- 在输入和输出上验证
- 安全失败并清晰错误
- 记录验证失败用于监控
传输安全
TLS要求
TLS配置:
最低:TLS 1.2
优选:TLS 1.3
必需设置:
□ 仅强密码套件
□ 完美前向保密(ECDHE)
□ 来自可信CA的有效证书
□ 启用HSTS头
□ 证书透明度
头信息:
Strict-Transport-Security: max-age=31536000; includeSubDomains
证书固定
证书固定(移动/嵌入式):
目的:即使CA受损也能防止MITM攻击
选项:
1. 固定证书
- 证书轮换需要更新
2. 固定公钥
- 证书续订后仍有效
- 更灵活
3. 固定中间CA
- 安全性和灵活性的平衡
考虑:
- 总有备用固定
- 计划轮换
- 优雅处理失败
- 考虑使用动态固定
速率限制和滥用防护
速率限制策略:
按身份:
- 每API密钥
- 每用户ID
- 每客户端ID
按资源:
- 每端点
- 每操作类型
- 每资源ID
头信息(草案标准):
RateLimit-Limit: 100
RateLimit-Remaining: 95
RateLimit-Reset: 1735689600
Retry-After: 60
响应码:
429 请求过多 - 速率限制超出
503 服务不可用 - 服务器过载
滥用防护:
□ 速率限制
□ 请求大小限制
□ 敏感操作CAPTCHA
□ 行为分析
□ IP声誉
□ 异常检测
安全头信息
基本API安全头信息:
# 防止MIME类型嗅探
X-Content-Type-Options: nosniff
# 控制敏感数据缓存
Cache-Control: no-store, private
# CORS配置(限制性)
Access-Control-Allow-Origin: https://trusted.example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Authorization, Content-Type
# 框架保护(如提供HTML)
X-Frame-Options: DENY
# 内容安全策略(如提供HTML)
Content-Security-Policy: default-src 'none'
相关技能
zero-trust-architecture- 整体安全架构mtls-service-mesh- 服务到服务安全rate-limiting-patterns- 详细速率限制策略secrets-management- 管理API密钥和秘密