{
  "id": "security/oauth2-state-parameter-replay-attack",
  "signature": "OAuth2 state parameter reuse allows CSRF via replay attack",
  "signature_zh": "OAuth2 state参数重用允许通过重放攻击进行CSRF",
  "regex": "state.*reuse|OAuth.*CSRF|state.*replay",
  "domain": "security",
  "category": "auth_error",
  "subcategory": null,
  "root_cause": "When the OAuth2 state parameter is not bound to a specific user session or is reused across multiple authorization requests, an attacker can replay a captured authorization response to bind a victim's account to the attacker's session.",
  "root_cause_type": "generic",
  "root_cause_zh": "当OAuth2 state参数未绑定到特定用户会话或跨多个授权请求重用时，攻击者可以重放捕获的授权响应，将受害者的账户绑定到攻击者的会话。",
  "versions": [
    {
      "version": "OAuth2 2.0",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    },
    {
      "version": "Spring Security OAuth2 5.8.0",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    },
    {
      "version": "Passport.js 0.7.0",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    }
  ],
  "os_specific": {},
  "dead_ends": [
    {
      "action": "Using a static state value for all users",
      "why_fails": "A static state is predictable and can be easily forged by an attacker; it doesn't provide CSRF protection.",
      "fail_rate": 1.0,
      "condition": "",
      "sources": []
    },
    {
      "action": "Generating state but not storing it in the session",
      "why_fails": "Without session binding, the state cannot be verified on callback; any state value is accepted, enabling replay.",
      "fail_rate": 0.95,
      "condition": "",
      "sources": []
    }
  ],
  "workarounds": [
    {
      "action": "Generate a cryptographically random state value per authorization request, store it in the user session, and validate it on callback. Example in Node.js: const state = crypto.randomBytes(16).toString('hex'); req.session.oauthState = state; res.redirect(`https://provider/authorize?response_type=code&client_id=...&state=${state}`);",
      "success_rate": 0.98,
      "how": "Generate a cryptographically random state value per authorization request, store it in the user session, and validate it on callback. Example in Node.js: const state = crypto.randomBytes(16).toString('hex'); req.session.oauthState = state; res.redirect(`https://provider/authorize?response_type=code&client_id=...&state=${state}`);",
      "condition": "",
      "sources": []
    },
    {
      "action": "Use a nonce-like mechanism where state includes a timestamp and user ID, signed with a server secret, to prevent replay.",
      "success_rate": 0.85,
      "how": "Use a nonce-like mechanism where state includes a timestamp and user ID, signed with a server secret, to prevent replay.",
      "condition": "",
      "sources": []
    }
  ],
  "workarounds_zh": [
    "每次授权请求生成加密随机的state参数，存储在用户会话中，并在回调时验证。Node.js示例：const state = crypto.randomBytes(16).toString('hex'); req.session.oauthState = state; res.redirect(`https://provider/authorize?response_type=code&client_id=...&state=${state}`);",
    "使用类似nonce的机制，state包含时间戳和用户ID，并用服务器密钥签名，以防止重放。"
  ],
  "transition_graph": {
    "leads_to": [],
    "preceded_by": [],
    "frequently_confused_with": []
  },
  "official_doc_url": "https://datatracker.ietf.org/doc/html/rfc6749#section-10.12",
  "official_doc_section": null,
  "error_code": null,
  "verification_tier": "ai_generated",
  "confidence": 0.83,
  "fix_success_rate": 0.88,
  "resolvable": "true",
  "first_seen": "2024-01-15",
  "last_confirmed": "2024-06-01",
  "last_updated": "2024-06-01",
  "evidence_count": 1,
  "tags": [],
  "locale": "en",
  "aliases": []
}