🔑 OAuth 2.0

OAuth 2.0 是应用程序接口访问最广泛使用的授权框架。选择合适的授权类型至关重要。

选择资助类型

资助类型 使用案例 安全等级
Authorization Code + PKCE SPA 和移动应用程序(推荐) 最高
Authorization Code 服务器端网络应用程序
Client Credentials 服务器到服务器通信(M2M) 中型
隐式(已废弃) 遗留的 SPA
资源所有者密码(已弃用) 仅高度可信的第一方
不使用隐式赠款

隐式授予(Implicit Grant)已被弃用,因为令牌已在 URL 片段中公开。对 SPA 使用授权码 + PKCE。

授权码 + PKCE 流程

JavaScript 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(JSON 网络令牌)

JWT 作为一种无状态身份验证令牌被广泛使用,但实施错误会直接导致安全漏洞。

JWT 安全注意事项

✅ 做

  • 明确指定算法(HS256 / RS256)
  • 设置较短的过期(exp)时间(15 分钟或更短)。
  • 验证发行人 (iss) 和受众 (aud)
  • 生成足够长度(256 位或更长)的密匙
  • 对刷新令牌实施轮换

❌ 不要

  • 在 JWT 有效负载中存储敏感信息(Base64 并非加密技术)
  • 允许算法无
  • 在本地存储中存储令牌(易受 XSS 影响)
  • 忽视令牌撤销管理
  • 使用 RS256 公钥作为 HS256 密钥

安全 JWT 实现

JavaScript (Node.js) 代币发行与验证
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 + 安全 + SameSite Cookie

最安全的方法是将访问令牌存储在带有 HttpOnly、Secure 和 SameSite=Strict 属性的 cookie 中。这样可以防止通过 XSS 攻击窃取令牌。

🗝 API 密钥管理

API 密钥是一种简单的身份验证机制,但管理不当会导致严重的风险。

API 密钥最佳实践

发电和储存

以足够的熵生成(256 位或更多)。存储散列文本;切勿在数据库中保存纯文本。

范围限制

为每个密钥分配最小范围(只读、仅限特定资源等)。避免通配符权限。

旋转

定期更换钥匙(90 天内)。为旧钥匙设定宽限期。一旦泄漏,立即吊销。

传输方式

通过标头(X-API-Key 或授权)发送。请勿包含在 URL 查询参数中(它们会显示在日志中)。

环境变量管理

不要在源代码中硬编码密钥。使用环境变量或秘密管理器来管理它们。

监控和记录

监控 API 密钥的使用情况。检测异常模式(大量请求、来自未知 IP 的访问)。

API 密钥验证实施示例

JavaScript (Express) 哈希 API 密钥验证
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();
}

🤖 Securing LLM & AI Agent API Access

AI agents and LLM-powered applications require additional authentication and authorization patterns beyond traditional API security.

Model-Specific Scopes

Assign different access levels per model tier (e.g., GPT-4 requires elevated scope). Prevent unauthorized use of expensive or sensitive models.

Token Budget Enforcement

Embed token budgets in access tokens or API key metadata. Enforce per-request and per-day limits to prevent denial-of-wallet attacks.

Prompt-Level Authorization

Check user permissions before executing certain prompt types (e.g., code generation, data analysis). Map prompt categories to role-based permissions.

Multi-Tenant Isolation

Ensure conversation history, fine-tuned models, and RAG data are isolated per tenant. Use tenant-scoped API keys with namespace enforcement.

Agent Identity & Attestation

Assign cryptographic identities to AI agents. Use signed JWTs for inter-agent communication. Verify agent identity before granting tool access.

Credential Rotation for AI Services

Automate API key rotation for LLM providers. Use short-lived tokens for agent sessions. Revoke credentials immediately when agents are decommissioned.

Agent Tool Access Authorization

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

Scoped OAuth Token for AI Agents (Python)

Python Agent Token Issuance with Scope Enforcement
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
OWASP References

Related: LLM06: Excessive Agency, ASI01: Excessive Agency, ASI03: Insecure Tool/Function Calling

验证方法比较

方法 无国籍 使用案例 安全考虑因素
OAuth 2.0 + PKCE 需要用户授权的应用程序接口 需要状态/PKCE,令牌到期管理
JWT Bearer 微型服务之间的交流 需要算法规范,有效载荷中无敏感数据
API密钥 服务器到服务器、外部集成 需要轮换和范围限制
mTLS 高度安全的 M2M 通信 证书管理运营成本高
会话 Cookie 传统网络应用程序 需要 CSRF 保护,可扩展性面临挑战