GHSA-22W3-693W-X895
Vulnerability from github – Published: 2026-05-06 23:31 – Updated: 2026-05-06 23:31Summary
webauthn-rs-core (Relying Party) and webauthn-authenticator-rs (client) checked that an Origin in CollectedClientData is valid for an RP ID with str::ends_with(), without checking for a dot (.) before the RP ID when allowing subdomains.
This check is flawed, and could allow requests from an attacker-controlled domain such as hermit-crab.example to be accepted for the RP ID crab.example (assuming .example was publicly-registerable TLD) when the RP allows authenticating from a subdomain (disabled by default in webauthn-rs-core and webauthn-rs).
- In
webauthn-rs-core, this only applies when: WebauthnCore::allow_subdomains_originistrue(the default isfalse), and- the attacker could register a domain that ends with the RP ID as a raw string, and,
- the client does not implement these checks correctly either
webauthn-rs can set allow_subdomains_origin via WebauthnBuilder::allow_subdomains. Fixing the bug in webauthn-rs-core also fixes it in webauthn-rs.
- In
webauthn-authenticator-rs, the flawed check is inWebauthnAuthenticator::do_registration()anddo_authentication().
A conforming Relying Party implementation would reject such requests, but webauthn-rs-core did not.
An application can still provide an incorrect origin parameter to webauthn-authenticator-rs, or use lower-level APIs that bypass these checks entirely, and this is by design.
These issues are a violation of WebAuthn Level 3 §13.4.9, §5.1.3 Step 8 and §5.1.4.1 Step 7.
Details
webauthn-rs-core/src/core.rsline 1213:
https://github.com/kanidm/webauthn-rs/blob/197cddf8487a2dbdd5d374e50d80aa1e4682f3ab/webauthn-rs-core/src/core.rs#L1211-L1216
webauthn-authenticator-rs/src/lib.rsline 274:
https://github.com/kanidm/webauthn-rs/blob/197cddf8487a2dbdd5d374e50d80aa1e4682f3ab/webauthn-authenticator-rs/src/lib.rs#L274-L277
webauthn-authenticator-rs/src/lib.rsline 335:
https://github.com/kanidm/webauthn-rs/blob/197cddf8487a2dbdd5d374e50d80aa1e4682f3ab/webauthn-authenticator-rs/src/lib.rs#L335-L338
str::ends_with() performs a raw string suffix match without enforcing a domain label boundary:
| Origin | RP ID | Expected result | Result with incorrect ends_with check |
|---|---|---|---|
hermit-crab.example |
crab.example |
rejected | accepted (bug!) |
auth.crab.example |
crab.example |
accepted | accepted (subdomain) |
crab.example |
crab.example |
accepted | accepted (exact match) |
hermit-crab.example |
auth.crab.example |
rejected | rejected |
auth.crab.example |
auth.crab.example |
accepted | accepted (exact match) |
Fix
When webauthn-rs-core v0.5.5 checks if an Origin is a valid subdomain of an RP ID, it will check that it ends with the RP ID prefixed with a dot (.{rp_id}). webauthn-rs v0.5.5 will be fixed by depending on webauthn-rs-core v0.5.5.
webauthn-authenticator-rs v0.5.5 now uses webauthn-rs-core's checks in WebauthnAuthenticator.
Regression tests for this bug have been added to both libraries.
Impact
With a both a non-conforming client implementation and vulnerable version of webauthn-rs-core configured to allow subdomains (not the default), this bug would allow an attacker at hermit-crab.example to phish a target's credential for the RP ID crab.example by directly proxying a legitimate navigator.credentials.get() request on the attacker's domain.
However, conforming client implementations (ie: all web browsers) will refuse to process WebAuthn requests for an RP ID that does not match the Origin of the current page and is not a related Origin.
In the scenario above with conforming client-side checks, this would force the attacker to change the request's RP ID to hermit-crab.example (the attacker's Origin). This would also change the RP ID hash, and webauthn-rs-core would reject it (per WebAuthn §7.2 Step 15).
Severity
Per WebAuthn §13.4.9:
The Relying Party MUST NOT accept unexpected values of origin, as doing so could allow a malicious website to obtain valid credentials. Although the scope of WebAuthn credentials prevents their use on domains outside the RP ID they were registered for, the Relying Party’s origin validation serves as an additional layer of protection in case a faulty authenticator fails to enforce credential scope.
Unfortunately, the chain needed to exploit this bug makes it difficult to classify with the CVSS framework. Kanidm came up with anywhere between "low" and "high" depending on the approach, and GitHub only provides one CVSS field for everything.
An attacker could easily bypass a correctly-implemented server-side Origin check, if they can convince a target to use their authenticator with an attacker-controlled client device or buggy/malicious client application. FIDO's Security Reference assumes that "the FIDO user device and applications involved in a FIDO operation are trustworthy agents of the user", and violating that limits the protections FIDO can provide, so it would be ridiculous to describe those bypasses as a "high" severity vulnerability.
However, webauthn-rs-core should take reasonable steps to prevent these sorts of issues where it can, especially when they're part of the WebAuthn specification.
Due to the complex preconditions and non-default configuration required to execute a successful attack, and that it is not exploitable in popular web browsers, Kanidm considers this a low severity issue.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.5.4"
},
"package": {
"ecosystem": "crates.io",
"name": "webauthn-rs-core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.5.5"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "crates.io",
"name": "webauthn-authenticator-rs"
},
"ranges": [
{
"events": [
{
"introduced": "0.6.0-dev"
},
{
"fixed": "0.6.1-dev"
}
],
"type": "ECOSYSTEM"
}
],
"versions": [
"0.6.0-dev"
]
},
{
"package": {
"ecosystem": "crates.io",
"name": "webauthn-rs-core"
},
"ranges": [
{
"events": [
{
"introduced": "0.6.0-dev"
},
{
"fixed": "0.6.1-dev"
}
],
"type": "ECOSYSTEM"
}
],
"versions": [
"0.6.0-dev"
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.5.4"
},
"package": {
"ecosystem": "crates.io",
"name": "webauthn-authenticator-rs"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.5.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-1289"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-06T23:31:27Z",
"nvd_published_at": null,
"severity": "LOW"
},
"details": "### Summary\n\n`webauthn-rs-core` ([Relying Party][rp]) and `webauthn-authenticator-rs` ([client][]) checked that [an `Origin` in `CollectedClientData`][origin] is valid for [an RP ID][rpid] with [`str::ends_with()`][ends-with], [without checking for a dot (`.`) before the RP ID when allowing subdomains][registerable-suffix].\n\nThis check is flawed, and could allow requests from an attacker-controlled domain such as `hermit-crab.example` to be accepted for the RP ID `crab.example` (assuming `.example` was publicly-registerable TLD) when the RP allows authenticating from a subdomain (disabled by default in `webauthn-rs-core` and `webauthn-rs`).\n\n[registerable-suffix]: https://html.spec.whatwg.org/multipage/browsers.html#is-a-registrable-domain-suffix-of-or-is-equal-to\n[ends-with]: https://doc.rust-lang.org/stable/std/primitive.str.html#method.ends_with\n[client]: https://www.w3.org/TR/webauthn-3/#client\n[rp]: https://www.w3.org/TR/webauthn-3/#relying-party\n[rpid]: https://www.w3.org/TR/webauthn-3/#rp-id\n[origin]: https://www.w3.org/TR/webauthn-3/#dom-collectedclientdata-origin\n\n* In `webauthn-rs-core`, this **only** applies when:\n * [`WebauthnCore::allow_subdomains_origin`](https://docs.rs/webauthn-rs-core/0.5.4/webauthn_rs_core/struct.WebauthnCore.html#method.new_unsafe_experts_only) is `true` (the default is `false`), *and*\n * the attacker could register a domain that ends with the RP ID as a raw string, *and*,\n * the client does not implement these checks correctly either\n\n `webauthn-rs` can set `allow_subdomains_origin` via [`WebauthnBuilder::allow_subdomains`](https://docs.rs/webauthn-rs/0.5.4/webauthn_rs/struct.WebauthnBuilder.html#method.allow_subdomains). Fixing the bug in `webauthn-rs-core` also fixes it in `webauthn-rs`.\n\n* In `webauthn-authenticator-rs`, the flawed check is in [`WebauthnAuthenticator::do_registration()`](https://docs.rs/webauthn-authenticator-rs/0.5.4/webauthn_authenticator_rs/struct.WebauthnAuthenticator.html#method.do_registration) and [`do_authentication()`](https://docs.rs/webauthn-authenticator-rs/0.5.4/webauthn_authenticator_rs/struct.WebauthnAuthenticator.html#method.do_registration). \n\n A conforming [Relying Party][rp] implementation would reject such requests, but `webauthn-rs-core` did not.\n\n An application can still provide an incorrect `origin` parameter to `webauthn-authenticator-rs`, or use lower-level APIs that bypass these checks entirely, and this is by design.\n\nThese issues are a violation of [WebAuthn Level 3 \u00a713.4.9](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin), [\u00a75.1.3 Step 8](https://www.w3.org/TR/webauthn-3/#CreateCred-DetermineRpId) and [\u00a75.1.4.1 Step 7](https://www.w3.org/TR/webauthn-3/#GetAssn-DetermineRpId).\n\n### Details\n\n* `webauthn-rs-core/src/core.rs` line 1213:\n\n https://github.com/kanidm/webauthn-rs/blob/197cddf8487a2dbdd5d374e50d80aa1e4682f3ab/webauthn-rs-core/src/core.rs#L1211-L1216\n\n* `webauthn-authenticator-rs/src/lib.rs` line 274:\n\n https://github.com/kanidm/webauthn-rs/blob/197cddf8487a2dbdd5d374e50d80aa1e4682f3ab/webauthn-authenticator-rs/src/lib.rs#L274-L277\n\n* `webauthn-authenticator-rs/src/lib.rs` line 335:\n\n https://github.com/kanidm/webauthn-rs/blob/197cddf8487a2dbdd5d374e50d80aa1e4682f3ab/webauthn-authenticator-rs/src/lib.rs#L335-L338\n\n[`str::ends_with()`][ends-with] performs a raw string suffix match _without_ [enforcing a domain label boundary][registerable-suffix]:\n\nOrigin | RP ID | Expected result | Result with incorrect `ends_with` check\n-- | -- | -- | --\n`hermit-crab.example` | `crab.example` | rejected | accepted **(bug!)**\n`auth.crab.example` | `crab.example` | accepted | accepted (subdomain)\n`crab.example` | `crab.example` | accepted | accepted (exact match)\n`hermit-crab.example` | `auth.crab.example` | rejected | rejected\n`auth.crab.example` | `auth.crab.example` | accepted | accepted (exact match)\n\n### Fix\n\nWhen `webauthn-rs-core` v0.5.5 checks if an `Origin` is a valid subdomain of an RP ID, it will check that it ends with the RP ID prefixed with a dot (`.{rp_id}`). `webauthn-rs` v0.5.5 will be fixed by depending on `webauthn-rs-core` v0.5.5.\n\n`webauthn-authenticator-rs` v0.5.5 now uses `webauthn-rs-core`\u0027s checks in `WebauthnAuthenticator`.\n\nRegression tests for this bug have been added to both libraries.\n\n### Impact\n\nWith a **both** a non-conforming [client][] implementation and vulnerable version of `webauthn-rs-core` configured to allow subdomains (not the default), this bug would allow an attacker at `hermit-crab.example` to phish a target\u0027s credential for the RP ID `crab.example` by directly proxying a legitimate `navigator.credentials.get()` request on the attacker\u0027s domain.\n\nHowever, _conforming_ [client implementations][client] (ie: all web browsers) will refuse to process WebAuthn requests for an RP ID that does not match the `Origin` of the current page and is not [a related `Origin`](https://www.w3.org/TR/webauthn-3/#sctn-related-origins).\n\nIn the scenario above with _conforming_ client-side checks, this would force the attacker to change the request\u0027s RP ID to `hermit-crab.example` (the attacker\u0027s `Origin`). This would also change the RP ID hash, [and `webauthn-rs-core` would reject it](https://github.com/kanidm/webauthn-rs/blob/197cddf8487a2dbdd5d374e50d80aa1e4682f3ab/webauthn-rs-core/src/core.rs#L825-L840) (per [WebAuthn \u00a77.2 Step 15](https://www.w3.org/TR/webauthn-3/#rp-op-verifying-assertion-step-rpid-hash)).\n\n### Severity\n\nPer [WebAuthn \u00a713.4.9](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin):\n\n\u003e The [Relying Party](https://www.w3.org/TR/webauthn-3/#relying-party) MUST NOT accept unexpected values of [origin](https://www.w3.org/TR/webauthn-3/#dom-collectedclientdata-origin), as doing so could allow a malicious website to obtain valid [credentials](https://www.w3.org/TR/credential-management-1/#concept-credential). Although the [scope](https://www.w3.org/TR/webauthn-3/#scope) of WebAuthn credentials prevents their use on domains outside the [RP ID](https://www.w3.org/TR/webauthn-3/#rp-id) they were registered for, the [Relying Party](https://www.w3.org/TR/webauthn-3/#relying-party)\u2019s origin validation serves as an additional layer of protection in case a faulty [authenticator](https://www.w3.org/TR/webauthn-3/#authenticator) fails to enforce credential [scope](https://www.w3.org/TR/webauthn-3/#scope).\n\nUnfortunately, [the chain needed to exploit this bug makes it difficult to classify with the CVSS framework](https://www.first.org/cvss/v4.0/user-guide#Vulnerability-Chaining). Kanidm came up with anywhere between \"low\" and \"high\" depending on the approach, and GitHub only provides one CVSS field for everything.\n\nAn attacker could easily bypass a _correctly-implemented_ server-side `Origin` check, if they can convince a target to use their authenticator with [an attacker-controlled client device](https://fidoalliance.org/specs/common-specs/fido-security-ref-v2.1-ps-20220523.html#dfn-t-1.3.2) or [buggy/malicious client application](https://fidoalliance.org/specs/common-specs/fido-security-ref-v2.1-ps-20220523.html#dfn-t-1.3.1). FIDO\u0027s Security Reference [assumes that \"the FIDO user device and applications involved in a FIDO operation are trustworthy agents of the user\"](https://fidoalliance.org/specs/common-specs/fido-security-ref-v2.1-ps-20220523.html#dfn-sa-4), and violating that [limits the protections FIDO can provide](https://fidoalliance.org/specs/common-specs/fido-security-ref-v2.1-ps-20220523.html#discussion), so it would be ridiculous to describe those bypasses as a \"high\" severity vulnerability.\n\nHowever, `webauthn-rs-core` should take reasonable steps to prevent these sorts of issues where it can, especially when they\u0027re part of the WebAuthn specification.\n\nDue to the complex preconditions and non-default configuration required to execute a successful attack, and that it is *not exploitable* in popular web browsers, Kanidm considers this a **low severity** issue.",
"id": "GHSA-22w3-693w-x895",
"modified": "2026-05-06T23:31:27Z",
"published": "2026-05-06T23:31:27Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/kanidm/webauthn-rs/security/advisories/GHSA-22w3-693w-x895"
},
{
"type": "PACKAGE",
"url": "https://github.com/kanidm/webauthn-rs"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "webauthn-rs-core/webauthn-authenticator-rs: Origin validation mismatch possible when subdomains are allowed"
}
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.