API 身份验证和授权实用指南,包括 OAuth 2.0、JWT 和 API 密钥管理。
OAuth 2.0 是应用程序接口访问最广泛使用的授权框架。选择合适的授权类型至关重要。
| 资助类型 | 使用案例 | 安全等级 |
|---|---|---|
| Authorization Code + PKCE | SPA 和移动应用程序(推荐) | 最高 |
| Authorization Code | 服务器端网络应用程序 | 高 |
| Client Credentials | 服务器到服务器通信(M2M) | 中型 |
| 隐式(已废弃) | 遗留的 SPA | 低 |
| 资源所有者密码(已弃用) | 仅高度可信的第一方 | 低 |
隐式授予(Implicit Grant)已被弃用,因为令牌已在 URL 片段中公开。对 SPA 使用授权码 + PKCE。
// 1. 随机生成代码验证器 function generateCodeVerifier() { const array = new Uint8Array(32); crypto.getRandomValues(array); return btoa(String.fromCharCode(...array)) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } // 2. 用 SHA-256 生成代码挑战 async function generateCodeChallenge(verifier) { const encoder = new TextEncoder(); const data = encoder.encode(verifier); const digest = await crypto.subtle.digest('SHA-256', data); return btoa(String.fromCharCode(...new Uint8Array(digest))) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } // 3. 授权请求 const verifier = generateCodeVerifier(); const challenge = await generateCodeChallenge(verifier); const authUrl = `https://auth.example.com/authorize?` + `response_type=code&` + `client_id=${clientId}&` + `redirect_uri=${redirectUri}&` + `code_challenge=${challenge}&` + `code_challenge_method=S256&` + `scope=read write`;
JWT 作为一种无状态身份验证令牌被广泛使用,但实施错误会直接导致安全漏洞。
const jwt = require('jsonwebtoken'); // --- 代币发行 --- function issueTokens(user) { const accessToken = jwt.sign( { sub: user.id, role: user.role }, process.env.JWT_SECRET, { algorithm: 'HS256', expiresIn: '15m', issuer: 'api.example.com', audience: 'app.example.com', } ); const refreshToken = jwt.sign( { sub: user.id, type: 'refresh' }, process.env.REFRESH_SECRET, { algorithm: 'HS256', expiresIn: '7d' } ); return { accessToken, refreshToken }; } // --- 令牌验证 --- function verifyAccessToken(token) { return jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'], // ← 始终指定! issuer: 'api.example.com', audience: 'app.example.com', }); }
| 存储位置 | 抵御 XSS | 抵御 CSRF | 建议 |
|---|---|---|---|
| HttpOnly Cookie | ◎ | △(需要 SameSite 设置) | 推荐 |
| 内存(可变) | ◎ | ◎ | 页面导航丢失 |
| localStorage | ✕(可通过 XSS 窃取) | ◎ | 不推荐 |
| sessionStorage | ✕(可通过 XSS 窃取) | ◎ | 有限使用 |
最安全的方法是将访问令牌存储在带有 HttpOnly、Secure 和 SameSite=Strict 属性的 cookie 中。这样可以防止通过 XSS 攻击窃取令牌。
API 密钥是一种简单的身份验证机制,但管理不当会导致严重的风险。
以足够的熵生成(256 位或更多)。存储散列文本;切勿在数据库中保存纯文本。
为每个密钥分配最小范围(只读、仅限特定资源等)。避免通配符权限。
定期更换钥匙(90 天内)。为旧钥匙设定宽限期。一旦泄漏,立即吊销。
通过标头(X-API-Key 或授权)发送。请勿包含在 URL 查询参数中(它们会显示在日志中)。
不要在源代码中硬编码密钥。使用环境变量或秘密管理器来管理它们。
监控 API 密钥的使用情况。检测异常模式(大量请求、来自未知 IP 的访问)。
const crypto = require('crypto'); // 使用 SHA-256 对应用程序接口密钥进行散列并比较 async function validateApiKey(req, res, next) { const apiKey = req.headers['x-api-key']; if (!apiKey) { return res.status(401).json({ error: 'API key required' }); } // 哈希值并在数据库中查找(由于我们比较哈希值,因此不需要 timingSafeEqual) const hashedKey = crypto .createHash('sha256') .update(apiKey) .digest('hex'); const keyRecord = await db.findApiKey(hashedKey); if (!keyRecord || keyRecord.revokedAt) { return res.status(403).json({ error: 'Invalid API key' }); } // 检查有效期 if (keyRecord.expiresAt && new Date() > keyRecord.expiresAt) { return res.status(403).json({ error: 'API key expired' }); } req.apiClient = keyRecord; next(); }
AI agents and LLM-powered applications require additional authentication and authorization patterns beyond traditional API security.
Assign different access levels per model tier (e.g., GPT-4 requires elevated scope). Prevent unauthorized use of expensive or sensitive models.
Embed token budgets in access tokens or API key metadata. Enforce per-request and per-day limits to prevent denial-of-wallet attacks.
Check user permissions before executing certain prompt types (e.g., code generation, data analysis). Map prompt categories to role-based permissions.
Ensure conversation history, fine-tuned models, and RAG data are isolated per tenant. Use tenant-scoped API keys with namespace enforcement.
Assign cryptographic identities to AI agents. Use signed JWTs for inter-agent communication. Verify agent identity before granting tool access.
Automate API key rotation for LLM providers. Use short-lived tokens for agent sessions. Revoke credentials immediately when agents are decommissioned.
| Scenario | Auth Method | Required Scope | Approval |
|---|---|---|---|
| Agent reads public data | API Key | data:read |
Automatic |
| Agent modifies user data | OAuth 2.0 (delegated) | data:write |
User consent required |
| Agent calls external API | OAuth 2.0 + mTLS | external:invoke |
Human-in-the-loop |
| Agent executes code / shell | Scoped JWT + Sandbox | exec:sandbox |
Admin approval + audit log |
import jwt import time from datetime import datetime, timedelta class AgentTokenIssuer: """Issues scoped, short-lived tokens for AI agents.""" ALLOWED_SCOPES = { "reader": ["data:read"], "writer": ["data:read", "data:write"], "executor": ["data:read", "data:write", "exec:sandbox"], } def __init__(self, secret_key: str): self.secret_key = secret_key def issue_agent_token( self, agent_id: str, role: str, tenant_id: str, token_budget: int = 10000, ttl_minutes: int = 15, ) -> str: # Validate role and resolve scopes if role not in self.ALLOWED_SCOPES: raise ValueError(f"Invalid role: {role}") payload = { "sub": agent_id, "tenant": tenant_id, "scopes": self.ALLOWED_SCOPES[role], "token_budget": token_budget, "iat": datetime.utcnow(), "exp": datetime.utcnow() + timedelta(minutes=ttl_minutes), "type": "agent", } return jwt.encode(payload, self.secret_key, algorithm="HS256") def verify_tool_access(self, token: str, required_scope: str) -> dict: # Decode and verify agent token payload = jwt.decode( token, self.secret_key, algorithms=["HS256"] ) if required_scope not in payload["scopes"]: raise PermissionError( f"Agent {payload['sub']} lacks scope: {required_scope}" ) return payload
Related: LLM06: Excessive Agency, ASI01: Excessive Agency, ASI03: Insecure Tool/Function Calling
| 方法 | 无国籍 | 使用案例 | 安全考虑因素 |
|---|---|---|---|
| OAuth 2.0 + PKCE | ○ | 需要用户授权的应用程序接口 | 需要状态/PKCE,令牌到期管理 |
| JWT Bearer | ○ | 微型服务之间的交流 | 需要算法规范,有效载荷中无敏感数据 |
| API密钥 | ○ | 服务器到服务器、外部集成 | 需要轮换和范围限制 |
| mTLS | ○ | 高度安全的 M2M 通信 | 证书管理运营成本高 |
| 会话 Cookie | ✕ | 传统网络应用程序 | 需要 CSRF 保护,可扩展性面临挑战 |