GHSA-F7PM-6HR8-7GGM
Vulnerability from github – Published: 2026-03-10 01:19 – Updated: 2026-03-10 22:21Summary
When allowed_origins is configured, CheckAllowedOrigins reduces URL-like values to their host component and accepts on host match alone. This makes exact origin policies impossible to express: scheme and port differences are silently ignored.
Details
CheckAllowedOrigins stores each configured allowed origin as:
parse_url($allowedOrigin)['host'] ?? $allowedOrigin
and later reduces the received clientDataJSON.origin the same way:
parse_url($C->origin)['host'] ?? $C->origin
If the reduced value matches, the method returns early. As a result, for the normal allowed_origins path, the later HTTPS check is not reached.
This differs from WebAuthn Level 2, which requires verifying that C.origin matches the RP's origin (scheme + host + port), separately from verifying that authData.rpIdHash matches the expected RP ID.
Affected code: - CheckAllowedOrigins.php
Spec references: - §7.1 Registering a New Credential - §7.2 Verifying an Authentication Assertion - CollectedClientData.origin
PoC
Configuration:
webauthn:
allowed_origins:
- https://login.example.com:8443
allow_subdomains: false
Send a registration or authentication response whose clientDataJSON.origin is:
https://login.example.com:9443
Observed: the response is accepted, because both values are reduced to login.example.com.
Expected: the response should be rejected, because https://login.example.com:8443 and https://login.example.com:9443 are different origins.
Impact
This is an origin validation error (CWE-346) affecting deployments that use allowed_origins. The most practical browser-facing scenario is same-host / different-port origin confusion. In non-browser or custom clients, scheme confusion may also be relevant.
Fix
Fixed in version 5.2.4 by rewriting CheckAllowedOrigins to perform full origin comparison (scheme + host + port) as required by the WebAuthn spec:
- Origins configured with a scheme (e.g.
https://example.com:8443) are now stored and compared as fullscheme://host[:port]values, with default port normalization (443 for HTTPS, 80 for HTTP). - Origins configured without a scheme are still matched by host only, for backward compatibility.
- Subdomain matching now also verifies scheme and port consistency.
See commit b4cd9a43.
Mitigation
Upgrade to web-auth/webauthn-framework (or web-auth/webauthn-lib / web-auth/webauthn-symfony-bundle) >= 5.2.4.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "web-auth/webauthn-framework"
},
"ranges": [
{
"events": [
{
"introduced": "5.2.0"
},
{
"fixed": "5.2.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "web-auth/webauthn-lib"
},
"ranges": [
{
"events": [
{
"introduced": "5.2.0"
},
{
"fixed": "5.2.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "web-auth/webauthn-symfony-bundle"
},
"ranges": [
{
"events": [
{
"introduced": "5.2.0"
},
{
"fixed": "5.2.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-30964"
],
"database_specific": {
"cwe_ids": [
"CWE-346"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-10T01:19:46Z",
"nvd_published_at": "2026-03-10T18:18:55Z",
"severity": "MODERATE"
},
"details": "### Summary\nWhen `allowed_origins` is configured, `CheckAllowedOrigins` reduces URL-like values to their `host` component and accepts on host match alone. This makes exact origin policies impossible to express: scheme and port differences are silently ignored.\n\n### Details\n`CheckAllowedOrigins` stores each configured allowed origin as:\n\n```php\nparse_url($allowedOrigin)[\u0027host\u0027] ?? $allowedOrigin\n```\n\nand later reduces the received `clientDataJSON.origin` the same way:\n\n```php\nparse_url($C-\u003eorigin)[\u0027host\u0027] ?? $C-\u003eorigin\n```\n\nIf the reduced value matches, the method returns early. As a result, for the normal `allowed_origins` path, the later HTTPS check is not reached.\n\nThis differs from [WebAuthn Level 2](https://www.w3.org/TR/webauthn-2/), which requires verifying that `C.origin` matches the RP\u0027s origin (scheme + host + port), separately from verifying that `authData.rpIdHash` matches the expected RP ID.\n\n**Affected code:**\n- [CheckAllowedOrigins.php](https://github.com/web-auth/webauthn-framework/blob/d58906e/src/webauthn/src/CeremonyStep/CheckAllowedOrigins.php)\n\n**Spec references:**\n- [\u00a77.1 Registering a New Credential](https://www.w3.org/TR/webauthn-2/#sctn-registering-a-new-credential)\n- [\u00a77.2 Verifying an Authentication Assertion](https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion)\n- [CollectedClientData.origin](https://www.w3.org/TR/webauthn-2/#dom-collectedclientdata-origin)\n\n### PoC\nConfiguration:\n\n```yaml\nwebauthn:\n allowed_origins:\n - https://login.example.com:8443\n allow_subdomains: false\n```\n\nSend a registration or authentication response whose `clientDataJSON.origin` is:\n\n```text\nhttps://login.example.com:9443\n```\n\n**Observed:** the response is accepted, because both values are reduced to `login.example.com`.\n\n**Expected:** the response should be rejected, because `https://login.example.com:8443` and `https://login.example.com:9443` are different origins.\n\n### Impact\nThis is an origin validation error (CWE-346) affecting deployments that use `allowed_origins`. The most practical browser-facing scenario is same-host / different-port origin confusion. In non-browser or custom clients, scheme confusion may also be relevant.\n\n### Fix\nFixed in version **5.2.4** by rewriting `CheckAllowedOrigins` to perform full origin comparison (scheme + host + port) as required by the WebAuthn spec:\n\n- Origins configured with a scheme (e.g. `https://example.com:8443`) are now stored and compared as full `scheme://host[:port]` values, with default port normalization (443 for HTTPS, 80 for HTTP).\n- Origins configured without a scheme are still matched by host only, for backward compatibility.\n- Subdomain matching now also verifies scheme and port consistency.\n\nSee commit [b4cd9a43](https://github.com/web-auth/webauthn-framework/commit/b4cd9a43).\n\n### Mitigation\nUpgrade to `web-auth/webauthn-framework` (or `web-auth/webauthn-lib` / `web-auth/webauthn-symfony-bundle`) **\u003e= 5.2.4**.",
"id": "GHSA-f7pm-6hr8-7ggm",
"modified": "2026-03-10T22:21:15Z",
"published": "2026-03-10T01:19:46Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/web-auth/webauthn-framework/security/advisories/GHSA-f7pm-6hr8-7ggm"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30964"
},
{
"type": "WEB",
"url": "https://github.com/web-auth/webauthn-framework/commit/535cc3c2dcbd9c3dfd5e00a254ad4a984e5e7839"
},
{
"type": "WEB",
"url": "https://github.com/web-auth/webauthn-framework/commit/b4cd9a4394c35fcac6080fd2f84f4f58a30abc01"
},
{
"type": "PACKAGE",
"url": "https://github.com/web-auth/webauthn-framework"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Webauthn Framework: allowed_origins collapses URL-like origins to host-only values, bypassing exact origin validation"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.