Low severityOSV Advisory· Published Jan 26, 2026· Updated Jan 27, 2026
sigstore has CSRF possibility in OIDC authentication during signing
CVE-2026-24408
Description
sigstore-python is a Python tool for generating and verifying Sigstore signatures. Prior to version 4.2.0, the sigstore-python OAuth authentication flow is susceptible to Cross-Site Request Forgery. _OAuthSession creates a unique "state" and sends it as a parameter in the authentication request but the "state" in the server response seems not not be cross-checked with this value. Version 4.2.0 contains a patch for the issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
sigstorePyPI | < 4.2.0 | 4.2.0 |
Affected products
1- Range: v0.0.1-pre.1, v0.0.1-pre.3, v0.1.0, …
Patches
15e77497fe8f0Merge commit from fork
4 files changed · +69 −0
CHANGELOG.md+3 −0 modified@@ -10,6 +10,9 @@ All versions prior to 0.9.0 are untracked. ### Fixed +* Add state validation to OIDC flow to prevent Cross-site request forgery + during OIDC authorization + ([GHSA-hm8f-75xx-w2vr](https://github.com/sigstore/sigstore-python/security/advisories/GHSA-hm8f-75xx-w2vr)) * verification now ensures that artifact digest documented in bundle and the real digest match (this is a bundle consistency check: bundle signature was always verified over real digest) ([#1652](https://github.com/sigstore/sigstore-python/pull/1652))
sigstore/_internal/oidc/oauth.py+4 −0 modified@@ -182,6 +182,10 @@ def __init__(self, client_id: str, client_secret: str, issuer: Issuer): base64.urlsafe_b64encode(os.urandom(32)).rstrip(b"=").decode() ) + @property + def state(self) -> str: + return self._state + @property def code_challenge(self) -> str: return B64Str(
sigstore/oidc.py+4 −0 modified@@ -312,6 +312,10 @@ def identity_token( # nosec: B107 raise IdentityError( f"Error response from auth endpoint: {auth_error[0]}" ) + + if server.auth_response["state"][0] != server.oauth_session.state: + raise IdentityError("OAuth state mismatch") + code = server.auth_response["code"][0] else: # In the out-of-band case, we wait until the user provides the code
test/unit/internal/oidc/test_issuer.py+58 −0 modified@@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from unittest.mock import MagicMock, patch + import pytest from sigstore.oidc import IdentityError, Issuer, IssuerError @@ -34,3 +36,59 @@ def test_get_identity_token_bad_code(monkeypatch): monkeypatch.setattr("builtins.input", lambda _: "hunter2") with pytest.raises(IdentityError, match=r"^Token request failed with .+$"): Issuer("https://oauth2.sigstage.dev/auth").identity_token(force_oob=True) + + +def test_identity_token_csrf_protection(): + """ + Verify that identity_token() raises IdentityError when the returned state + does not match the session state (CSRF protection). + """ + with ( + patch("sigstore.oidc.webbrowser.open"), + patch("sigstore._internal.oidc.oauth._OAuthFlow") as MockOAuthFlow, + patch("sigstore.oidc.requests.Session") as MockSession, + patch("sigstore.oidc.IdentityToken"), + ): + # Setup the mock server returned by _OAuthFlow context manager + mock_server = MagicMock() + MockOAuthFlow.return_value.__enter__.return_value = mock_server + + # Simulate a mismatching state + original_state = "original-secure-state" + malicious_state = "malicious-state" + + # The session has the original state (we mock the property access) + # Since we added a property 'state', we need to make sure the mock returns it. + # But here we are mocking the whole server object. + # server.oauth_session.state + mock_server.oauth_session.state = original_state + + mock_server.is_oob.return_value = False + mock_server.base_uri = "http://localhost:12345" + mock_server.redirect_uri = "http://localhost:12345/callback" + + # The auth response simulates what the redirect handler receives + mock_server.auth_response = { + "code": ["fake-code"], + "state": [malicious_state], + } + + # Mock responses for Issuer initialization and token exchange + mock_session_instance = MockSession.return_value + + # Mock .well-known/openid-configuration response + mock_config_response = MagicMock() + mock_config_response.json.return_value = { + "authorization_endpoint": "https://auth.example.com", + "token_endpoint": "https://token.example.com", + } + mock_config_response.raise_for_status.return_value = None + + mock_session_instance.get.side_effect = [mock_config_response] + + # Initialize Issuer + issuer = Issuer("https://issuer.example.com") + + # Call identity_token() and expect IdentityError due to state mismatch + with pytest.raises(IdentityError, match="OAuth state mismatch"): + issuer.identity_token()
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-hm8f-75xx-w2vrghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-24408ghsaADVISORY
- github.com/sigstore/sigstore-python/commit/5e77497fe8f0b202bdd118949074ec2f20da69aaghsax_refsource_MISCWEB
- github.com/sigstore/sigstore-python/releases/tag/v4.2.0ghsax_refsource_MISCWEB
- github.com/sigstore/sigstore-python/security/advisories/GHSA-hm8f-75xx-w2vrghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.