Repository navigation
Description
Description
The UNSAFE_PATH_PATTERN regex in RedirectUtils.java (line 153) catches literal path traversal sequences (../, ..;/) and their percent-encoded dot variants (%2E%2E), but does not cover percent-encoded terminators or control characters in the path. Since the check runs against URI.getRawPath(), the following encoded forms pass through unmatched:
%3B/%3b(encoded semicolon)%0A,%0D,%09,%00(control characters)%252E%252E(double-encoded dots)
While none of these cause actual path traversal on standard web servers (percent-encoded characters are treated as literals per RFC 3986, not as delimiters), tightening the regex provides defense-in-depth against non-conformant downstream stacks that may double-decode or treat encoded semicolons as path parameter separators.
Current regex
private final static Pattern UNSAFE_PATH_PATTERN = Pattern.compile(
"(/|%2[fF]|%5[cC]|\\\\)(%2[eE]|\\.){2}(/|%2[fF]|%5[cC]|\\\\|;)|(/|%2[fF]|%5[cC]|\\\\)(%2[eE]|\\.){2}$");The terminator group (/|%2[fF]|%5[cC]|\\\\|;) accepts literal ; but not %3B, %3b, or control-character bytes.
Suggested fix
Option A — extend the terminator character class to include %3[bB] and reject control characters:
private final static Pattern UNSAFE_PATH_PATTERN = Pattern.compile(
"(/|%2[fF]|%5[cC]|\\\\)(%2[eE]|\\.){2}(/|%2[fF]|%5[cC]|\\\\|;|%3[bB]|%0[aAdD09]|%00)" +
"|(/|%2[fF]|%5[cC]|\\\\)(%2[eE]|\\.){2}$");Option B — replace regex-only validation with canonical path normalization:
private static boolean areWildcardsAllowed(URI redirectUri) {
if (redirectUri.getRawUserInfo() != null) return false;
String raw = redirectUri.getRawPath();
if (raw == null) return true;
// Reject control characters in raw path
for (int i = 0; i < raw.length(); i++) {
char c = raw.charAt(i);
if (c < 0x20 || c == 0x7F) return false;
}
// Decode once and check for traversal in decoded form
String decoded = redirectUri.getPath();
if (decoded != null && decoded.contains("..")) return false;
return !UNSAFE_PATH_PATTERN.matcher(raw).find();
}Option B is more robust against future encoding variants but changes more code.
Affected file
services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java
Additional test cases
The following should be added to RedirectUtilsTest.testEncodedRedirectUri():
// Encoded semicolon variants
Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/..%3B/", set, false));
Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/..%3b/", set, false));
// Control character terminators
Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/..%0A/", set, false));
Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/..%09/", set, false));
Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/..%00/", set, false));
---
*This issue was originally tracked in the private repository. Migrated by @abstractj.*
Activity
Tighten UNSAFE_PATH_PATTERN against encoded path-traversal terminators
Update OAuthRedirectUriTest expectations for double-encoded dots
Tighten UNSAFE_PATH_PATTERN against encoded path-traversal terminators (
29 remaining items
Tighten UNSAFE_PATH_PATTERN against encoded path-traversal terminators (