{"id":"CVE-2026-49265","aliases":["GHSA-xpv3-w29h-x7cv"],"title":"Oauthlib: Timing Attack Vulnerability in PKCE code_verifier Comparison (CWE-208)","summary":"Oauthlib: Timing Attack Vulnerability in PKCE code_verifier Comparison (CWE-208)","severity":"medium","cvss":6.8,"cwe":["CWE-208"],"vendor":"oauthlib","product":"oauthlib","ecosystem":"pip","affected":["oauthlib >= 3.0.0, < 4.0.0"],"patched":["oauthlib 4.0.0"],"published":"2026-09-29","updated":"2026-09-29","sourceUpdated":"2026-09-29T17:56:32Z","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-xpv3-w29h-x7cv","references":[{"url":"https://github.com/oauthlib/oauthlib/security/advisories/GHSA-xpv3-w29h-x7cv"},{"url":"https://github.com/oauthlib/oauthlib/pull/963"},{"url":"https://github.com/oauthlib/oauthlib/commit/40b0ab56da3682c2484a4b78bbff309f8025d950"},{"url":"https://github.com/advisories/GHSA-xpv3-w29h-x7cv"}],"tags":["ghsa","pip"],"ingestedAt":"2026-09-29T18:42:35.834Z","slug":"CVE-2026-49265","body":"## Overview\n\n## Summary\n\nA timing side-channel vulnerability exists in the PKCE (RFC 7636) implementation \nof the Authorization Code Grant flow. The `code_challenge_method_plain` function \nuses Python's standard `==` operator for string comparison instead of a \nconstant-time comparison function, potentially allowing timing-based attacks.\n\n## Affected Component\n\n- File: `oauthlib/oauth2/rfc6749/grant_types/authorization_code.py`\n- Functions: `code_challenge_method_plain`, `code_challenge_method_s256`\n- Vulnerability Type: CWE-208 (Observable Timing Discrepancy)\n\n## Technical Details\n\nPython's `==` operator uses short-circuit evaluation when comparing strings:\n1. Returns `False` immediately if lengths differ\n2. Compares characters left-to-right, stopping at first mismatch\n\nThis means comparison time varies linearly with the length of the common prefix \nbetween the attacker-supplied verifier and the stored challenge, creating a \nmeasurable timing oracle.\n\n## Proof of Concept\n\nTested locally against oauthlib source (network jitter eliminated to isolate \npure Python execution time):\n\n| Input | Result | Time (10M iterations) |\n|---|---|---|\n| Wrong first char (`B` + `A`*49) | Fast reject | 0.34106s |\n| 49 chars correct (`A`*49 + `B`) | Deep compare | 0.37847s |\n| **Difference** | | **0.03741s** |\n\nThe ~37ms delta over 10M iterations corresponds to nanosecond-level differences \nper call, which are statistically exploitable under controlled conditions.\n\n## Attack Scenario\n\n1. Attacker intercepts `authorization_code` via Custom URI Scheme Hijacking\n2. PKCE blocks token request — attacker lacks `code_verifier`\n3. Attacker sends repeated requests to `/token` endpoint measuring response times\n4. Using timing oracle, attacker recovers `code_verifier` character by character\n5. Attacker obtains Access Token → Account Takeover\n\n> **Note:** Practical exploitability is limited due to the single-use nature of \n> authorization codes and real-world network noise. However, the vulnerable \n> pattern should be corrected as a defense-in-depth measure.\n\n## Recommended Fix\n\nReplace `==` with `hmac.compare_digest()` for constant-time comparison:\n\ncr: Elvin Latifli\n\n## Affected packages\n\n- `oauthlib >= 3.0.0, < 4.0.0`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `oauthlib 4.0.0`","depth":"sunlit","depthScore":37,"depthScoreParts":{"impact":37.4,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}