CVE-2026-115910: OpenID Connect Session-Binding Bypass and Account Takeover in Python Social Auth
Vulnerability ID: CVE-2026-115910
CVSS Score: 6.8
Published: 2026-10-28
A session-binding vulnerability in Python Social Auth (social-core) versions prior to 6.2.0 allows an attacker to perform authorization code injection against the OpenID Connect authentication backend. By storing generated OIDC nonces in a global database table without binding them to the initiating browser session, the library accepts any valid, unexpired nonce returned in an ID token regardless of which session originated the authentication transaction. When combined with the default disablement of Proof Key for Code Exchange (PKCE), an attacker who intercepts an unused authorization code can inject it into their own authentication flow to achieve full account takeover.
TL;DR
Python Social Auth (social-core < 6.2.0) fails to bind OpenID Connect nonces to the initiating user session, validating them only against a global database table. Attackers who intercept a victim's unused authorization code can inject it into their own session and bypass nonce verification, resulting in account takeover. Upgrade to social-auth-core 6.2.0 and enable PKCE to remediate.
⚠️ Exploit Status: POC
Technical Details
- Vulnerability Type: CWE-345 (Insufficient Verification of Data Authenticity) / CWE-384 (Session Fixation)
- Affected Component: OpenIdConnectAuth (social_core/backends/open_id_connect.py)
- Attack Vector: Network (Authorization Code Injection via OIDC Callback)
- CVSS v3.1 Score: 6.8 Medium (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N)
- EPSS Score: 0.16% (4.78th Percentile)
- Impact: Account Takeover (High Confidentiality and Integrity Impact)
- Exploit Status: Proof-of-Concept (Upstream Unit Test Suite)
- Patched Version: social-auth-core 6.2.0
Affected Systems
- Python Social Auth (social-core / social-auth-core)
-
social-core (social-auth-core): < 6.2.0 (Fixed in:
6.2.0)
Code Analysis
Commit: c2f2ae4
Bind OpenID Connect nonces to the initiating browser session and OAuth2 state, rejecting authorization codes from other login attempts.
File: social_core/backends/open_id_connect.py
@@ -253,7 +266,9 @@ def auth_params(self, state=None):
"""Return extra arguments needed on auth process."""
params = super().auth_params(state)
if self.USE_NONCE:
- params["nonce"] = self.get_and_store_nonce(self.authorization_url(), state)
+ nonce = self.get_and_store_nonce(self.authorization_url(), state)
+ self._store_session_nonce(state, nonce)
+ params["nonce"] = nonce
@@ -397,9 +428,26 @@ def validate_temporal_claims(self, id_token) -> None:
def validate_claims(self, id_token) -> None:
self.validate_temporal_claims(id_token)
- # Validate the nonce to ensure the request was not modified
+ # The token nonce must belong to the state validated for this browser.
nonce = id_token.get("nonce")
- if not nonce:
+ transaction = (
+ self._oidc_association_transaction
+ if self.ASSOCIATION_ONLY
+ else self._oauth2_transaction
+ )
+ expected = (
+ transaction.get("oidc_nonce") if isinstance(transaction, dict) else None
+ )
+ state = transaction.get("state") if isinstance(transaction, dict) else None
+ if not isinstance(nonce, str):
+ nonce = ""
+ if not isinstance(expected, str):
+ expected = ""
+ if not isinstance(state, str):
+ state = ""
+ if not (
+ nonce and expected and state and constant_time_compare(nonce, expected)
+ ):
raise AuthResponseError(
self,
"Incorrect id_token: nonce",
@@ -408,7 +456,7 @@ def validate_claims(self, id_token) -> None:
)
nonce_obj = self.get_nonce(nonce)
- if nonce_obj:
+ if nonce_obj and nonce_obj.assoc_type == state:
self.remove_nonce(nonce_obj.id)
else:
raise AuthResponseError(
Mitigation Strategies
- Upgrade social-auth-core to version 6.2.0 or later to enforce cryptographic session binding of OpenID Connect nonces.
- Enable Proof Key for Code Exchange (PKCE) across all OpenID Connect authentication backends to cryptographically bind authorization codes to the initiating client session.
- Ensure strict Referrer-Policy headers (e.g., strict-origin-when-cross-origin or no-referrer) are set on authentication callback pages to prevent authorization code leakage via HTTP Referer headers.
- Monitor application authentication logs for AuthResponseError exceptions containing the error code 'nonce_mismatch' at the 'token_validation' stage.
Remediation Steps:
- Update project dependency manifests (requirements.txt, pyproject.toml, or Pipfile) to require social-auth-core>=6.2.0.
- Run 'pip install --upgrade social-auth-core>=6.2.0' in all affected application environments and container images.
- Add 'SOCIAL_AUTH_OIDC_USE_PKCE = True' (or the corresponding backend-specific setting) to the application configuration to enforce PKCE during OAuth 2.0 / OIDC code exchanges.
- Restart application worker processes to load the patched library and verify that OIDC login flows complete without session context errors.
References
- NVD - CVE-2026-115910
- CVE.org Record - CVE-2026-115910
- Google OSV - GHSA-h2r5-x5m7-3935
- CVEListV5 Record - CVE-2026-115910
- GitHub Pull Request #2141 - python-social-auth/social-core
- GitHub Commit c2f2ae4663bef19ee8b07f2cf5a2344f9155d826
- Python Social Auth social-core 6.2.0 Release
Read the full report for CVE-2026-115910 on our website for more details including interactive diagrams and full exploit analysis.
Top comments (0)