Opened 17 months ago
Last modified 2 weeks ago
#63063 new defect (bug)
IDN domains are erroneously URL-encoded in the wp_sanitize_redirect() function
| Reported by: | calpeconsulting | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | Awaiting Review |
| Component: | Charset | Version: | 6.7.2 |
| Severity: | minor | Keywords: | has-patch has-unit-tests |
| Cc: | Focuses: |
Description
Overview
There is an issue with how Internationalised Domain Names (IDNs) are handled in the WordPress redirect system, specifically when an IDN is used in the "WordPress Address" or "Site Address" settings. The problem occurs when WordPress tries to redirect the user back to the post after submitting a comment. The domain part of the URL, which should remain in its IDN format, is incorrectly processed and URL-encoded by WordPress.
WordPress and IDNs
WordPress fully supports IDN domains in the General site settings (under "WordPress Address (URL)" and "Site Address (URL)"). These fields allow users to set an IDN domain (such as simon.schönbeck.dk) for their website without any issues.
The IDN domain should not undergo any transformation when used in URLs within WordPress, as the domain is already properly handled and encoded when set in the site's settings.
Redirection Process
When a comment is posted, WordPress triggers a redirect to the comment's location on the post. This is done using the $location variable, which contains the full URL (including the post's IDN domain).
The Problem
During this process, the function wp_sanitize_redirect() is called. This function is responsible for sanitising and cleaning up the redirect URL.
Unexpected Behaviour
The wp_sanitize_redirect() function calls _wp_sanitize_utf8_in_redirect(). This function URL-encodes any UTF-8 characters in the URL, which includes characters in the domain name (e.g., the ö in simon.schönbeck.dk is encoded as %C3%B6).
This transformation should not occur for IDN domains, as the domain part is already in a valid format (IDN is treated differently from regular UTF-8 encoding).
Effect
The problem arises because the sanitisation process applies URL encoding to the domain part of the IDN URL, such as converting characters like ö to %C3%B6. This encoding breaks the validation of the domain name in wp_validate_redirect(), which expects the domain to be in a valid, non-encoded format.
Since the domain with URL-encoded characters does not pass the validation checks, the fallback URL is triggered. By default, this fallback URL is set to the WordPress admin page (admin.php), resulting in the user being incorrectly redirected to the admin dashboard rather than back to the post they came from.
This issue mainly affects guest commentators who do not need to log in before commenting. After the comment is successfully submitted, but due to the failed URL validation, WordPress redirects them to the admin panel as a fallback URL. Since they are not logged in, they are then redirected to the login page, even though no login is required to post a comment.
Solution
The IDN domain part of the URL should not be sanitised or URL-encoded for UTF-8 characters, as it is already in a valid format. The sanitisation process should respect the IDN format, preventing unnecessary transformations that break the validation.
Workaround
Until a fix is implemented, a workaround is to manually encode your site/blog URL as Punycode in the "WordPress Address (URL)" and "Site Address (URL)" settings. This ensures that the domain part is in the correct format and avoids the encoding issues caused by the sanitisation process.
Attachments (1)
Change History (2)
#1
@
2 weeks ago
- Keywords has-patch has-unit-tests added; needs-patch removed
I ran into this same redirect problem while testing IDN support on a multilingual site, so figured I'd take a crack at a patch.
Digging into it, the actual bug is in wp_sanitize_redirect() over in wp-includes/pluggable.php. It runs the whole $location string, host included, through a regex that percent-encodes any raw multi-byte UTF-8 it finds, and then through a second pass that strips anything outside a fairly strict ASCII whitelist. Neither pass makes an exception for the host, so something like simon.schönbeck.dk comes out the other side with ö turned into %C3%B6. By the time wp_validate_redirect() tries to match that against allowed_redirect_hosts, the host no longer matches anything, so it falls back to admin_url() — which is exactly the wp-login bounce guest commenters are seeing.
The attached patch splits $location into the bit before the host, the host itself, and the bit after, using wp_parse_url( $location, PHP_URL_HOST ) to find it. Only the before/after pieces go through the two sanitizing passes; the host gets stitched back in untouched. If there's no host at all (relative redirects), nothing changes — the string just goes through the same two passes it always did.
I was a bit careful here since this function sits underneath basically every redirect in core, so I didn't want to loosen anything security-relevant. The CRLF and null-byte stripping still happen on the full, reassembled string afterward, so that protection isn't touched, and the host safelist check in wp_validate_redirect() is untouched too — this only stops the host from getting mangled before that check ever runs. I also went through the existing test cases in redirect.php by hand against the new logic (the IPv6 literal, the %0A/%0D nesting checks, the existing UTF-8-in-path test) and none of them change.
Added four tests in tests/phpunit/tests/formatting/redirect.php covering: the IDN host from the original report round-tripping unchanged, UTF-8 outside the host still getting encoded (so the fix doesn't overreach), CRLF still getting stripped when the host is IDN, and a sanity check that relative redirects are untouched.
Used 7.1.0 as a placeholder for the @since tag — happy to change it once there's a milestone.
AI assistance: Yes. Used an AI coding assistant to help trace the regex behavior and draft the patch/tests; I reviewed the logic line by line and manually checked it against the existing test cases before posting.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
Adds host-aware sanitization to wp_sanitize_redirect() so IDN hosts written in native Unicode aren't percent-encoded/stripped. Includes 4 new PHPUnit tests.