Skip to content

decode punycode public suffixes in PublicSuffixMatcher.verify - #876

Open
dxbjavid wants to merge 2 commits into
apache:masterfrom
dxbjavid:psl-verify-idn-normalise
Open

decode punycode public suffixes in PublicSuffixMatcher.verify#876
dxbjavid wants to merge 2 commits into
apache:masterfrom
dxbjavid:psl-verify-idn-normalise

Conversation

@dxbjavid

Copy link
Copy Markdown
Contributor

PublicSuffixMatcher.verify (through verifyInternal) resolves the domain against the suffix rules without the normalisation and punycode decoding that getDomainRoot and matches already apply, and the bundled list holds IDN suffixes in their Unicode form. So an ACE-encoded public suffix such as xn--h-2fa.no matches no rule and verify returns true, which lets the cookie PublicSuffixDomainFilter treat a whole IDN TLD as a registrable domain and accept a supercookie scoped to it, even though matches recognises the same suffix correctly. This decodes and lowercases the input in verifyInternal the same way getDomainRoot does, so both the ACE and Unicode forms are rejected consistently while genuine registrable subdomains under an IDN suffix are still allowed.

@ok2c ok2c left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dxbjavid Could we pull the normalization logic up into #verify method and leave #verifyInternal as is under assumption its input is always normalized?

Signed-off-by: Javid Khan <dxbjavid@gmail.com>
@dxbjavid

Copy link
Copy Markdown
Contributor Author

makes sense, done. normalisation now happens in verify and verifyInternal just assumes a normalised input.

one thing worth flagging: DefaultHostnameVerifier.matchIdentity calls verifyInternal directly rather than through verify, so with the logic moved up it now relies on the identity already being in normalised form. that matches the pre-existing behaviour on master, but if you'd like that path covered too i'm happy to normalise the identity at that call site.

// Unicode form. The rules are held that way, so an ACE-encoded (xn--) or mixed-case public
// suffix has to be decoded first, mirroring getDomainRoot; otherwise it fails to match a
// rule and is mistaken for a registrable domain.
String normalized = DnsUtils.normalize(domain.startsWith(".") ? domain.substring(1) : domain);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dxbjavid Could not we use DnsUtils#normalizeUnicode here?

@ok2c

ok2c commented Aug 23, 2026

Copy link
Copy Markdown
Member

one thing worth flagging: DefaultHostnameVerifier.matchIdentity calls verifyInternal directly rather than through verify, so with the logic moved up it now relies on the identity already being in normalised form. that matches the pre-existing behaviour on master, but if you'd like that path covered too i'm happy to normalise the identity at that call site.

The reason #verifyInternal exists at all is to avoid having to run the input through the normalization process multiple times. DefaultHostnameVerifier#matchIdentity normalizes the input at the beginning of the routine and then passes it normalized to the PSM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants