Spring Security Vulnerability Protection-HttpFirewall and HTTPS

1. HttpFirewall

Spring Security has several areas where the patterns you define are tested against incoming requests to determine how the request should be handled. This happens when the FilterChainProxy decides which filter chain the request should pass through, and when the FilterSecurityInterceptor decides which security constraints apply to the request. When testing against a pattern you define, it’s important to understand what the mechanism is and what URL values are used.

The servlet specification defines several properties for HttpServletRequest that are accessible via getter methods that we might want to match. These properties are contextPath, servletPath, pathInfo, and queryString. Spring Security is only interested in path security within the application, so contextPath is ignored. Unfortunately, the servlet specification does not define exactly what the servletPath and pathInfo` values contain for a specific request URI. For example, each path segment of the URL may contain parameters, as defined by RFC 2396 (you may have seen that when the browser does not support cookies, the jsessionid parameter is appended to the URL after the semicolon. However, the RFC allows these parameters appear in any path segment of the URL). The specification does not explicitly state whether these parameters should be included in the values of servletPath and pathInfo, and the behavior varies between different servlet containers. There is a danger that when an application is deployed in a container that does not strip path parameters from these values, an attacker can add them to the requested URL, causing pattern matching to succeed or fail unexpectedly. Once the request leaves the FilterChainProxy, the original value will be returned so the application can still use it). There may also be other changes to the incoming URL. For example, it may contain path traversal sequences (such as /../) or multiple forward slashes (//), which may also cause pattern matching to fail. Some containers will normalize these before executing the servlet mapping, but others will not. To prevent similar issues, FilterChainProxy uses the HttpFirewall policy to inspect and wrap requests. By default, unnormalized requests are automatically rejected, and path parameters and repeated slashes are removed for matching. So, for example, a raw request path of /secure;hack=1/somefile.html;hack=2 is returned as /secure/somefile.html). Therefore, a FilterChainProxy must be used to manage secure filter chains. Note that the servletPath and pathInfo values are decoded by the container, so your application should not have any valid paths containing semicolons, as these parts will be removed for matching purposes.

As mentioned before, the default strategy is to use Ant-style path matching, which is probably the best option for most users. This strategy is implemented in the AntPathRequestMatcher class, which uses Spring’s AntPathMatcher to perform a case-insensitive match on the connection pattern of the servletPath and pathInfo, ignoring the queryString.

If you need a more powerful matching strategy, you can use regular expressions. Then the implementation of this strategy is RegexRequestMatcher. See the Javadoc for this class for more information.

In practice, we recommend that you use method security at the service layer to control access to your application, rather than relying entirely on using security constraints defined at the web application layer. URLs change, and it’s difficult to account for all the possible URLs an application might support, and how requests might be manipulated. You should limit yourself to a few simple Ant paths that are easy to understand. Always try to use the “deny-by-default” approach, where you have a catch-all wildcard (/** or **) defined at the end to deny access.

Security defined at the service layer is stronger and harder to bypass, so you should always take advantage of Spring Security’s method security options.

HttpFirewall also prevents HTTP response splitting by rejecting newline characters in HTTP response headers.

By default, the StrictHttpFirewall implementation is used. This implementation will reject requests that appear to be malicious. If it’s too restrictive for your needs, you can customize which types of requests are rejected. However, it is important to know that doing so leaves your application vulnerable to attacks. For example, if you wish to use Spring MVC’s matrix variable, you can use the following configuration.

Allow Matrix Variables

  • Java
@Bean
public StrictHttpFirewall httpFirewall() {
    StrictHttpFirewall firewall = new StrictHttpFirewall();
    firewall.setAllowSemicolon(true);
    return firewall;
}

To prevent Cross-Site Tracing (XST) and HTTP Verb Tampering, StrictHttpFirewall provides a list of allowed valid HTTP methods. The default valid methods are DELETE, GET, HEAD, OPTIONS, PATCH, POST and `PUT. If your application requires modification of valid methods, you can configure a custom StrictHttpFirewall bean. The following example only allows HTTP GET and POST methods.

Allow Only GET & amp; POST

  • Java
@Bean
public StrictHttpFirewall httpFirewall() {
    StrictHttpFirewall firewall = new StrictHttpFirewall();
    firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST"));
    return firewall;
}

If you use new MockHttpServletRequest(), the HTTP method it currently creates is an empty string (“”). This is an invalid HTTP method and will be rejected by Spring Security. You can use new MockHttpServletRequest(“GET”, “”) instead to solve this problem. See SPR_16851, which asks to improve this.

If you must allow any HTTP method (not recommended), you can use StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true). Doing so disables validation of HTTP methods entirely.

StrictHttpFirewall also checks header names and values and parameter names. It requires that each character has a defined code point and is not a control character.

This requirement can be relaxed or adjusted if necessary by the following methods.

  • StrictHttpFirewall#setAllowedHeaderNames(Predicate)
  • StrictHttpFirewall#setAllowedHeaderValues(Predicate)
  • StrictHttpFirewall#setAllowedParameterNames(Predicate)

Parameter values can also be controlled using setAllowedParameterValues(Predicate).

For example, to turn off this check, you can add a Predicate instance to your StrictHttpFirewall that always returns true.

Allow Any Header Name, Header Value, and Parameter Name

  • Java
@Bean
public StrictHttpFirewall httpFirewall() {
    StrictHttpFirewall firewall = new StrictHttpFirewall();
    firewall.setAllowedHeaderNames((header) -> true);
    firewall.setAllowedHeaderValues((header) -> true);
    firewall.setAllowedParameterNames((parameter) -> true);
    return firewall;
}

Alternatively, there may be a specific value that you need to allow.

For example, the User-Agent used by iPhone X? includes a character that is not part of the ISO-8859-1 character set. Due to this fact, some application servers parse this value as two separate characters, the latter being an undefined character.

You can solve this problem using setAllowedHeaderValues method.

Allow Certain User Agents

  • Java
@Bean
public StrictHttpFirewall httpFirewall() {
    StrictHttpFirewall firewall = new StrictHttpFirewall();
    Pattern allowed = Pattern.compile("[\p{IsAssigned} & amp; & amp;[^\p{IsControl}]]]*");
    Pattern userAgent = ...;
    firewall.setAllowedHeaderValues((header) -> allowed.matcher(header).matches() || userAgent.matcher(header).matches());
    return firewall;
}

If it’s a header value, you might consider parsing it to UTF-8 when validating.

Parse Headers As UTF-8

  • Java
firewall.setAllowedHeaderValues((header) -> {
    String parsed = new String(header.getBytes(ISO_8859_1), UTF_8);
    return allowed.matcher(parsed).matches();
});

2. Redirect to HTTPS

If the client makes the request using HTTP instead of HTTPS, you can configure Spring Security to redirect to HTTPS.

For example, the following Java or Kotlin configuration redirects any HTTP request to HTTPS.

Redirect to HTTPS

  • Java

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.requiresChannel(channel -> channel
.anyRequest().requiresSecure()
);
return http.build();
}
}

The following XML configuration redirects all HTTP requests to HTTPS

Redirect to HTTPS with XML Configuration

<http>
<intercept-url pattern="/**" access="ROLE_USER" requires-channel="https"/>
...
</http>

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Network Skill TreeHomepageOverview 42010 people are learning the system