Skip to content

Return HTTP 400 for firewall-rejected requests instead of logging ERROR#47

Open
filipesantana18 wants to merge 2 commits into
OpenConext:mainfrom
filipesantana18:main
Open

Return HTTP 400 for firewall-rejected requests instead of logging ERROR#47
filipesantana18 wants to merge 2 commits into
OpenConext:mainfrom
filipesantana18:main

Conversation

@filipesantana18

Copy link
Copy Markdown

Summary

Automated scanners frequently send malformed request paths (containing ;, encoded
slashes, .env, .git/config, etc.). Spring Security's StrictHttpFirewall correctly
rejects these with a RequestRejectedException. However, the default
DefaultRequestRejectedHandler rethrows the exception, so it propagates up to
Tomcat's ErrorReportValve and is logged at ERROR with a full stack trace.

In deployments where a log/SMTP appender is bound at ERROR level, this produces alert
spam for expected, correctly-blocked traffic.

Current behavior (log)

A malicious scanner request results in the following ERROR being logged:

2026-07-17 07:47:04,813 ERROR [http-nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]:175 - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
    at org.springframework.security.web.firewall.StrictHttpFirewall.rejectedBlocklistedUrls(StrictHttpFirewall.java:535)
    at org.springframework.security.web.firewall.StrictHttpFirewall.getFirewalledRequest(StrictHttpFirewall.java:505)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:206)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
    ...
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    ...
    at java.base/java.lang.Thread.run(Thread.java:1583)

Change

Register a RequestRejectedHandler bean using the built-in
HttpStatusRequestRejectedHandler, so firewall rejections return a clean 400 Bad Request without an ERROR-level log or stack trace.

@Bean
public RequestRejectedHandler requestRejectedHandler() {
    return new HttpStatusRequestRejectedHandler();
}

The bean is auto-detected and wired into the FilterChainProxy by Spring Security's
WebSecurityConfiguration (Spring Security 6.4.x / Spring Boot 3.4.4).

Why this is safe

  • The StrictHttpFirewall protection is unchanged — malicious requests are still
    rejected exactly as before.
  • Only the rejection-to-response/logging behavior changes: a clean 400 instead of a
    propagated exception logged as ERROR.
  • No new attack surface, no weakened access control.

Tests

  • Added VootControllerTest.testFirewallRejectsMaliciousRequest() which sends a request
    with a malicious path (/me/groups;malicious=probe) and asserts a 400 response. The
    firewall blocks the request before authentication, so no token is required.

Verification

Before: firewall rejections logged at ERROR with a full RequestRejectedException
stack trace (see log above), and — where configured — triggered alert emails.
After: the same requests return 400 with no ERROR log.

Added a custom RequestRejectedHandler to return HTTP 400 for malicious requests.
Add test to verify firewall rejects malicious requests.
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.

1 participant