Finalizing Security Optimizations

This commit is contained in:
Tim Zöller 2026-04-07 12:49:46 +02:00
parent dc12425611
commit 897252f9cd

View file

@ -9,6 +9,8 @@ import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* Custom authentication entry point that handles unauthenticated requests.
@ -45,9 +47,42 @@ public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint
return;
}
// HTML page requests should redirect to login
// HTML page requests should redirect to login. The redirect parameter is built
// from the original request URI so the login page can later send the user back.
// We must restrict it to a single internal path anything starting with "//" or
// "/\" could be interpreted by browsers as a protocol-relative URL pointing at
// an external host, turning this into an open redirect.
log.debug("HTML page request - redirecting to /login");
String redirectUrl = "/login?redirect=" + requestUri;
String redirectUrl;
if (isSafeInternalPath(requestUri)) {
redirectUrl = "/login?redirect=" + URLEncoder.encode(requestUri, StandardCharsets.UTF_8);
} else {
log.warn("Refusing to propagate suspicious redirect target: {}", requestUri);
redirectUrl = "/login";
}
response.sendRedirect(redirectUrl);
}
/**
* Returns true if the given path is a safe single-slash internal path that can be
* round-tripped through a {@code redirect} query parameter without enabling an open
* redirect. Rejects null/empty, anything not starting with {@code /}, and anything
* starting with {@code //} or {@code /\} (both of which browsers may interpret as
* protocol-relative URLs to a different host).
*/
private static boolean isSafeInternalPath(String path) {
if (path == null || path.isEmpty()) {
return false;
}
if (path.charAt(0) != '/') {
return false;
}
if (path.length() >= 2) {
char second = path.charAt(1);
if (second == '/' || second == '\\') {
return false;
}
}
return true;
}
}