The _checkOrigin() method validates the origin by checking if the RP ID is a suffix of the host (source):
return \preg_match('/' . \preg_quote($this->_rpId) . '$/i', $host) === 1;
This matches any hostname ending with the RP ID string, without requiring a domain boundary. For RP ID example.com, the following origins
incorrectly pass validation:
https://evil-example.com (lookalike domain)
https://evilexample.com (lookalike domain)
The W3C WebAuthn Level 2 spec (§7.1 Step 5 / §7.2 Step 9) requires the RP ID to be equal to the origin's effective domain, or the origin's
host to be a subdomain of the RP ID — meaning a dot boundary is required.
Proposed fix:
if (\strcasecmp($host, $this->_rpId) === 0) {
return true;
}
return \str_ends_with(\strtolower($host), '.' . \strtolower($this->_rpId));
This accepts example.com (exact match) and www.example.com (subdomain at dot boundary), while rejecting evil-example.com and evilexample.com (lookalikes).
The
_checkOrigin()method validates the origin by checking if the RP ID is a suffix of the host (source):This matches any hostname ending with the RP ID string, without requiring a domain boundary. For RP ID
example.com, the following originsincorrectly pass validation:
https://evil-example.com(lookalike domain)https://evilexample.com(lookalike domain)The W3C WebAuthn Level 2 spec (§7.1 Step 5 / §7.2 Step 9) requires the RP ID to be equal to the origin's effective domain, or the origin's
host to be a subdomain of the RP ID — meaning a dot boundary is required.
Proposed fix:
This accepts
example.com(exact match) andwww.example.com(subdomain at dot boundary), while rejectingevil-example.comandevilexample.com(lookalikes).