Refactor Duplicate Security Filter Chain Doc

This removes the duplicate Security Filter Chain Doc and moves the
HttpFirewall to exploits portion of the documentation.

Closes gh-7979
This commit is contained in:
Rob Winch 2020-02-12 12:57:26 -06:00
parent ea6a0635ef
commit 72a9c15278
5 changed files with 115 additions and 256 deletions

View File

@ -94,8 +94,7 @@ For that reason, if you are attempting to troubleshoot Spring Security's Servlet
Second, since `FilterChainProxy` is central to Spring Security usage it can perform tasks that are not viewed as optional.
// FIXME: Add a link to SecurityContext
For example, it clears out the `SecurityContext` to avoid memory leaks.
// FIXME: Add a link to HttpFirewall
It also applies Spring Security's `HttpFirewall` to protect applications against certain types of attacks.
It also applies Spring Security's <<servlet-httpfirewall,`HttpFirewall`>> to protect applications against certain types of attacks.
In addition, it provides more flexibility in determining when a `SecurityFilterChain` should be invoked.
// FIXME: Add link to SecurityFitlerChain
@ -168,8 +167,6 @@ Below is a comprehensive list of Spring Security Filter ordering:
include::technical-overview.adoc[]
include::security-filter-chain.adoc[]
include::core-filters.adoc[]
include::core-services.adoc[]

View File

@ -1,251 +0,0 @@
[[security-filter-chain]]
== The Security Filter Chain
Spring Security's web infrastructure is based entirely on standard servlet filters.
It doesn't use servlets or any other servlet-based frameworks (such as Spring MVC) internally, so it has no strong links to any particular web technology.
It deals in `HttpServletRequest` s and `HttpServletResponse` s and doesn't care whether the requests come from a browser, a web service client, an `HttpInvoker` or an AJAX application.
Spring Security maintains a filter chain internally where each of the filters has a particular responsibility and filters are added or removed from the configuration depending on which services are required.
The ordering of the filters is important as there are dependencies between them.
If you have been using <<ns-config,namespace configuration>>, then the filters are automatically configured for you and you don't have to define any Spring beans explicitly but here may be times when you want full control over the security filter chain, either because you are using features which aren't supported in the namespace, or you are using your own customized versions of classes.
[[delegating-filter-proxy]]
=== DelegatingFilterProxy
When using servlet filters, you obviously need to declare them in your `web.xml`, or they will be ignored by the servlet container.
In Spring Security, the filter classes are also Spring beans defined in the application context and thus able to take advantage of Spring's rich dependency-injection facilities and lifecycle interfaces.
Spring's `DelegatingFilterProxy` provides the link between `web.xml` and the application context.
When using `DelegatingFilterProxy`, you will see something like this in the `web.xml` file:
[source,xml]
----
<filter>
<filter-name>myFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
----
Notice that the filter is actually a `DelegatingFilterProxy`, and not the class that will actually implement the logic of the filter.
What `DelegatingFilterProxy` does is delegate the `Filter` 's methods through to a bean which is obtained from the Spring application context.
This enables the bean to benefit from the Spring web application context lifecycle support and configuration flexibility.
The bean must implement `javax.servlet.Filter` and it must have the same name as that in the `filter-name` element.
Read the Javadoc for `DelegatingFilterProxy` for more information
[[filter-chain-proxy]]
=== FilterChainProxy
Spring Security's web infrastructure should only be used by delegating to an instance of `FilterChainProxy`.
The security filters should not be used by themselves.
In theory you could declare each Spring Security filter bean that you require in your application context file and add a corresponding `DelegatingFilterProxy` entry to `web.xml` for each filter, making sure that they are ordered correctly, but this would be cumbersome and would clutter up the `web.xml` file quickly if you have a lot of filters.
`FilterChainProxy` lets us add a single entry to `web.xml` and deal entirely with the application context file for managing our web security beans.
It is wired using a `DelegatingFilterProxy`, just like in the example above, but with the `filter-name` set to the bean name "filterChainProxy".
The filter chain is then declared in the application context with the same bean name.
Here's an example:
[source,xml]
----
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>
<list>
<sec:filter-chain pattern="/restful/**" filters="
securityContextPersistenceFilterWithASCFalse,
basicAuthenticationFilter,
exceptionTranslationFilter,
filterSecurityInterceptor" />
<sec:filter-chain pattern="/**" filters="
securityContextPersistenceFilterWithASCTrue,
formLoginFilter,
exceptionTranslationFilter,
filterSecurityInterceptor" />
</list>
</constructor-arg>
</bean>
----
The namespace element `filter-chain` is used for convenience to set up the security filter chain(s) which are required within the application.
footnote:[Note that you'll need to include the security namespace in your application context XML file in order to use this syntax.
The older syntax which used a `filter-chain-map` is still supported, but is deprecated in favour of the constructor argument injection.].
It maps a particular URL pattern to a list of filters built up from the bean names specified in the `filters` element, and combines them in a bean of type `SecurityFilterChain`.
The `pattern` attribute takes an Ant Paths and the most specific URIs should appear first footnote:[Instead of a path pattern, the `request-matcher-ref` attribute can be used to specify a `RequestMatcher` instance for more powerful matching].
At runtime the `FilterChainProxy` will locate the first URI pattern that matches the current web request and the list of filter beans specified by the `filters` attribute will be applied to that request.
The filters will be invoked in the order they are defined, so you have complete control over the filter chain which is applied to a particular URL.
You may have noticed we have declared two `SecurityContextPersistenceFilter` s in the filter chain (`ASC` is short for `allowSessionCreation`, a property of `SecurityContextPersistenceFilter`).
As web services will never present a `jsessionid` on future requests, creating `HttpSession` s for such user agents would be wasteful.
If you had a high-volume application which required maximum scalability, we recommend you use the approach shown above.
For smaller applications, using a single `SecurityContextPersistenceFilter` (with its default `allowSessionCreation` as `true`) would likely be sufficient.
Note that `FilterChainProxy` does not invoke standard filter lifecycle methods on the filters it is configured with.
We recommend you use Spring's application context lifecycle interfaces as an alternative, just as you would for any other Spring bean.
When we looked at how to set up web security using <<ns-web-xml,namespace configuration>>, we used a `DelegatingFilterProxy` with the name "springSecurityFilterChain".
You should now be able to see that this is the name of the `FilterChainProxy` which is created by the namespace.
==== Bypassing the Filter Chain
You can use the attribute `filters = "none"` as an alternative to supplying a filter bean list.
This will omit the request pattern from the security filter chain entirely.
Note that anything matching this path will then have no authentication or authorization services applied and will be freely accessible.
If you want to make use of the contents of the `SecurityContext` contents during a request, then it must have passed through the security filter chain.
Otherwise the `SecurityContextHolder` will not have been populated and the contents will be null.
=== Filter Ordering
The order that filters are defined in the chain is very important.
Irrespective of which filters you are actually using, the order should be as follows:
* `ChannelProcessingFilter`, because it might need to redirect to a different protocol
* `SecurityContextPersistenceFilter`, so a `SecurityContext` can be set up in the `SecurityContextHolder` at the beginning of a web request, and any changes to the `SecurityContext` can be copied to the `HttpSession` when the web request ends (ready for use with the next web request)
* `ConcurrentSessionFilter`, because it uses the `SecurityContextHolder` functionality and needs to update the `SessionRegistry` to reflect ongoing requests from the principal
* Authentication processing mechanisms - `UsernamePasswordAuthenticationFilter`, `CasAuthenticationFilter`, `BasicAuthenticationFilter` etc - so that the `SecurityContextHolder` can be modified to contain a valid `Authentication` request token
* The `SecurityContextHolderAwareRequestFilter`, if you are using it to install a Spring Security aware `HttpServletRequestWrapper` into your servlet container
* The `JaasApiIntegrationFilter`, if a `JaasAuthenticationToken` is in the `SecurityContextHolder` this will process the `FilterChain` as the `Subject` in the `JaasAuthenticationToken`
* `RememberMeAuthenticationFilter`, so that if no earlier authentication processing mechanism updated the `SecurityContextHolder`, and the request presents a cookie that enables remember-me services to take place, a suitable remembered `Authentication` object will be put there
* `AnonymousAuthenticationFilter`, so that if no earlier authentication processing mechanism updated the `SecurityContextHolder`, an anonymous `Authentication` object will be put there
* `ExceptionTranslationFilter`, to catch any Spring Security exceptions so that either an HTTP error response can be returned or an appropriate `AuthenticationEntryPoint` can be launched
* `FilterSecurityInterceptor`, to protect web URIs and raise exceptions when access is denied
[[request-matching]]
=== Request Matching and HttpFirewall
Spring Security has several areas where patterns you have defined are tested against incoming requests in order to decide how the request should be handled.
This occurs when the `FilterChainProxy` decides which filter chain a request should be passed through and also when the `FilterSecurityInterceptor` decides which security constraints apply to a request.
It's important to understand what the mechanism is and what URL value is used when testing against the patterns that you define.
The Servlet Specification defines several properties for the `HttpServletRequest` which are accessible via getter methods, and which we might want to match against.
These are the `contextPath`, `servletPath`, `pathInfo` and `queryString`.
Spring Security is only interested in securing paths within the application, so the `contextPath` is ignored.
Unfortunately, the servlet spec does not define exactly what the values of `servletPath` and `pathInfo` will contain for a particular request URI.
For example, each path segment of a URL may contain parameters, as defined in https://www.ietf.org/rfc/rfc2396.txt[RFC 2396]
footnote:[You have probably seen this when a browser doesn't support cookies and the `jsessionid` parameter is appended to the URL after a semi-colon.
However the RFC allows the presence of these parameters in any path segment of the URL].
The Specification does not clearly state whether these should be included in the `servletPath` and `pathInfo` values and the behaviour varies between different servlet containers.
There is a danger that when an application is deployed in a container which does not strip path parameters from these values, an attacker could add them to the requested URL in order to cause a pattern match to succeed or fail unexpectedly.
footnote:[The original values will be returned once the request leaves the `FilterChainProxy`, so will still be available to the application.].
Other variations in the incoming URL are also possible.
For example, it could contain path-traversal sequences (like `/../`) or multiple forward slashes (`//`) which could also cause pattern-matches to fail.
Some containers normalize these out before performing the servlet mapping, but others don't.
To protect against issues like these, `FilterChainProxy` uses an `HttpFirewall` strategy to check and wrap the request.
Un-normalized requests are automatically rejected by default, and path parameters and duplicate slashes are removed for matching purposes.
footnote:[So, for example, an original request path `/secure;hack=1/somefile.html;hack=2` will be returned as `/secure/somefile.html`.].
It is therefore essential that a `FilterChainProxy` is used to manage the security filter chain.
Note that the `servletPath` and `pathInfo` values are decoded by the container, so your application should not have any valid paths which contain semi-colons, as these parts will be removed for matching purposes.
As mentioned above, the default strategy is to use Ant-style paths for matching and this is likely to be the best choice for most users.
The strategy is implemented in the class `AntPathRequestMatcher` which uses Spring's `AntPathMatcher` to perform a case-insensitive match of the pattern against the concatenated `servletPath` and `pathInfo`, ignoring the `queryString`.
If for some reason, you need a more powerful matching strategy, you can use regular expressions.
The strategy implementation is then `RegexRequestMatcher`.
See the Javadoc for this class for more information.
In practice we recommend that you use method security at your service layer, to control access to your application, and do not rely entirely on the use of security constraints defined at the web-application level.
URLs change and it is difficult to take account of all the possible URLs that an application might support and how requests might be manipulated.
You should try and restrict yourself to using a few simple ant paths which are simple to understand.
Always try to use a "deny-by-default" approach where you have a catch-all wildcard ( /** or **) defined last and denying access.
Security defined at the service layer is much more robust and harder to bypass, so you should always take advantage of Spring Security's method security options.
The `HttpFirewall` also prevents https://www.owasp.org/index.php/HTTP_Response_Splitting[HTTP Response Splitting] by rejecting new line characters in the HTTP Response headers.
By default the `StrictHttpFirewall` is used.
This implementation rejects requests that appear to be malicious.
If it is too strict for your needs, then you can customize what types of requests are rejected.
However, it is important that you do so knowing that this can open your application up to attacks.
For example, if you wish to leverage Spring MVC's Matrix Variables, the following configuration could be used in XML:
[source,xml]
----
<b:bean id="httpFirewall"
class="org.springframework.security.web.firewall.StrictHttpFirewall"
p:allowSemicolon="true"/>
<http-firewall ref="httpFirewall"/>
----
The same thing can be achieved with Java Configuration by exposing a `StrictHttpFirewall` bean.
[source,java]
----
@Bean
public StrictHttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowSemicolon(true);
return firewall;
}
----
The `StrictHttpFirewall` provides a whitelist of valid HTTP methods that are allowed to protect against https://www.owasp.org/index.php/Cross_Site_Tracing[Cross Site Tracing (XST)] and https://www.owasp.org/index.php/Test_HTTP_Methods_(OTG-CONFIG-006)[HTTP Verb Tampering].
The default valid methods are "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", and "PUT".
If your application needs to modify the valid methods, you can configure a custom `StrictHttpFirewall` bean.
For example, the following will only allow HTTP "GET" and "POST" methods:
[source,xml]
----
<b:bean id="httpFirewall"
class="org.springframework.security.web.firewall.StrictHttpFirewall"
p:allowedHttpMethods="GET,HEAD"/>
<http-firewall ref="httpFirewall"/>
----
The same thing can be achieved with Java Configuration by exposing a `StrictHttpFirewall` bean.
[source,java]
----
@Bean
public StrictHttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST"));
return firewall;
}
----
[TIP]
====
If you are using `new MockHttpServletRequest()` it currently creates an HTTP method as an empty String "".
This is an invalid HTTP method and will be rejected by Spring Security.
You can resolve this by replacing it with `new MockHttpServletRequest("GET", "")`.
See https://jira.spring.io/browse/SPR-16851[SPR_16851] for an issue requesting to improve this.
====
If you must allow any HTTP method (not recommended), you can use `StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true)`.
This will disable validation of the HTTP method entirely.
=== Use with other Filter-Based Frameworks
If you're using some other framework that is also filter-based, then you need to make sure that the Spring Security filters come first.
This enables the `SecurityContextHolder` to be populated in time for use by the other filters.
Examples are the use of SiteMesh to decorate your web pages or a web framework like Wicket which uses a filter to handle its requests.
[[filter-chains-with-ns]]
=== Advanced Namespace Configuration
As we saw earlier in the namespace chapter, it's possible to use multiple `http` elements to define different security configurations for different URL patterns.
Each element creates a filter chain within the internal `FilterChainProxy` and the URL pattern that should be mapped to it.
The elements will be added in the order they are declared, so the most specific patterns must again be declared first.
Here's another example, for a similar situation to that above, where the application supports both a stateless RESTful API and also a normal web application which users log into using a form.
[source,xml]
----
<!-- Stateless RESTful service using Basic authentication -->
<http pattern="/restful/**" create-session="stateless">
<intercept-url pattern='/**' access="hasRole('REMOTE')" />
<http-basic />
</http>
<!-- Empty filter chain for the login page -->
<http pattern="/login.htm*" security="none"/>
<!-- Additional filter chain for normal users, matching all other requests -->
<http>
<intercept-url pattern='/**' access="hasRole('USER')" />
<form-login login-page='/login.htm' default-target-url="/home.htm"/>
<logout />
</http>
----

View File

@ -0,0 +1,111 @@
[[servlet-httpfirewall]]
= HttpFirewall
Spring Security has several areas where patterns you have defined are tested against incoming requests in order to decide how the request should be handled.
This occurs when the `FilterChainProxy` decides which filter chain a request should be passed through and also when the `FilterSecurityInterceptor` decides which security constraints apply to a request.
It's important to understand what the mechanism is and what URL value is used when testing against the patterns that you define.
The Servlet Specification defines several properties for the `HttpServletRequest` which are accessible via getter methods, and which we might want to match against.
These are the `contextPath`, `servletPath`, `pathInfo` and `queryString`.
Spring Security is only interested in securing paths within the application, so the `contextPath` is ignored.
Unfortunately, the servlet spec does not define exactly what the values of `servletPath` and `pathInfo` will contain for a particular request URI.
For example, each path segment of a URL may contain parameters, as defined in https://www.ietf.org/rfc/rfc2396.txt[RFC 2396]
footnote:[You have probably seen this when a browser doesn't support cookies and the `jsessionid` parameter is appended to the URL after a semi-colon.
However the RFC allows the presence of these parameters in any path segment of the URL].
The Specification does not clearly state whether these should be included in the `servletPath` and `pathInfo` values and the behaviour varies between different servlet containers.
There is a danger that when an application is deployed in a container which does not strip path parameters from these values, an attacker could add them to the requested URL in order to cause a pattern match to succeed or fail unexpectedly.
footnote:[The original values will be returned once the request leaves the `FilterChainProxy`, so will still be available to the application.].
Other variations in the incoming URL are also possible.
For example, it could contain path-traversal sequences (like `/../`) or multiple forward slashes (`//`) which could also cause pattern-matches to fail.
Some containers normalize these out before performing the servlet mapping, but others don't.
To protect against issues like these, `FilterChainProxy` uses an `HttpFirewall` strategy to check and wrap the request.
Un-normalized requests are automatically rejected by default, and path parameters and duplicate slashes are removed for matching purposes.
footnote:[So, for example, an original request path `/secure;hack=1/somefile.html;hack=2` will be returned as `/secure/somefile.html`.].
It is therefore essential that a `FilterChainProxy` is used to manage the security filter chain.
Note that the `servletPath` and `pathInfo` values are decoded by the container, so your application should not have any valid paths which contain semi-colons, as these parts will be removed for matching purposes.
As mentioned above, the default strategy is to use Ant-style paths for matching and this is likely to be the best choice for most users.
The strategy is implemented in the class `AntPathRequestMatcher` which uses Spring's `AntPathMatcher` to perform a case-insensitive match of the pattern against the concatenated `servletPath` and `pathInfo`, ignoring the `queryString`.
If for some reason, you need a more powerful matching strategy, you can use regular expressions.
The strategy implementation is then `RegexRequestMatcher`.
See the Javadoc for this class for more information.
In practice we recommend that you use method security at your service layer, to control access to your application, and do not rely entirely on the use of security constraints defined at the web-application level.
URLs change and it is difficult to take account of all the possible URLs that an application might support and how requests might be manipulated.
You should try and restrict yourself to using a few simple ant paths which are simple to understand.
Always try to use a "deny-by-default" approach where you have a catch-all wildcard ( /** or **) defined last and denying access.
Security defined at the service layer is much more robust and harder to bypass, so you should always take advantage of Spring Security's method security options.
The `HttpFirewall` also prevents https://www.owasp.org/index.php/HTTP_Response_Splitting[HTTP Response Splitting] by rejecting new line characters in the HTTP Response headers.
By default the `StrictHttpFirewall` is used.
This implementation rejects requests that appear to be malicious.
If it is too strict for your needs, then you can customize what types of requests are rejected.
However, it is important that you do so knowing that this can open your application up to attacks.
For example, if you wish to leverage Spring MVC's Matrix Variables, the following configuration could be used:
.Allow Matrix Variables
====
.Java
[source,java,role="primary"]
----
@Bean
public StrictHttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowSemicolon(true);
return firewall;
}
----
.XML
[source,xml,role="secondary"]
----
<b:bean id="httpFirewall"
class="org.springframework.security.web.firewall.StrictHttpFirewall"
p:allowSemicolon="true"/>
<http-firewall ref="httpFirewall"/>
----
====
The `StrictHttpFirewall` provides an allowed list of valid HTTP methods that are allowed to protect against https://www.owasp.org/index.php/Cross_Site_Tracing[Cross Site Tracing (XST)] and https://www.owasp.org/index.php/Test_HTTP_Methods_(OTG-CONFIG-006)[HTTP Verb Tampering].
The default valid methods are "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", and "PUT".
If your application needs to modify the valid methods, you can configure a custom `StrictHttpFirewall` bean.
For example, the following will only allow HTTP "GET" and "POST" methods:
.Allow Only GET & POST
====
.Java
[source,java,role="primary"]
----
@Bean
public StrictHttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST"));
return firewall;
}
----
.XML
[source,xml,role="secondary"]
----
<b:bean id="httpFirewall"
class="org.springframework.security.web.firewall.StrictHttpFirewall"
p:allowedHttpMethods="GET,HEAD"/>
<http-firewall ref="httpFirewall"/>
----
====
[TIP]
====
If you are using `new MockHttpServletRequest()` it currently creates an HTTP method as an empty String "".
This is an invalid HTTP method and will be rejected by Spring Security.
You can resolve this by replacing it with `new MockHttpServletRequest("GET", "")`.
See https://jira.spring.io/browse/SPR-16851[SPR_16851] for an issue requesting to improve this.
====
If you must allow any HTTP method (not recommended), you can use `StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true)`.
This will disable validation of the HTTP method entirely.

View File

@ -5,3 +5,5 @@ include::csrf.adoc[leveloffset=+1]
include::headers.adoc[leveloffset=+1]
include::http.adoc[leveloffset=+1]
include::firewall.adoc[leveloffset=+1]

View File

@ -126,7 +126,7 @@ All you need to enable web security to begin with is
Which says that we want all URLs within our application to be secured, requiring the role `ROLE_USER` to access them, we want to log in to the application using a form with username and password, and that we want a logout URL registered which will allow us to log out of the application.
`<http>` element is the parent for all web-related namespace functionality.
The `<intercept-url>` element defines a `pattern` which is matched against the URLs of incoming requests using an ant path style syntax footnote:[See the section on pass:specialcharacters,macros[<<request-matching>>] in the Web Application Infrastructure chapter for more details on how matches are actually performed.].
The `<intercept-url>` element defines a `pattern` which is matched against the URLs of incoming requests using an ant path style syntax footnote:[See the section on pass:specialcharacters,macros[<<servlet-httpfirewall,`HttpFirewall`>>] for more details on how matches are actually performed.].
You can also use regular-expression matching as an alternative (see the namespace appendix for more details).
The `access` attribute defines the access requirements for requests matching the given pattern.
With the default configuration, this is typically a comma-separated list of roles, one of which a user must have to be allowed to make the request.