SEC-1584: Documentation of request-checking and matching process. Logging of servletPath and and pathInfo in DebugFilter for comparison.

This commit is contained in:
Luke Taylor 2010-10-03 22:53:37 +01:00
parent 0fd2c48dfb
commit f455e9a5a4
3 changed files with 112 additions and 42 deletions

View File

@ -41,6 +41,8 @@ class DebugFilter extends OncePerRequestFilter {
List<Filter> filters = getFilters(request);
logger.log("Request received for '" + UrlUtils.buildRequestUrl(request) + "':\n\n" +
request + "\n\n" +
"servletPath:" + request.getServletPath() + "\n" +
"pathInfo:" + request.getPathInfo() + "\n\n" +
formatFilters(filters));
fcp.doFilter(new DebugRequestWrapper(request), response, filterChain);

View File

@ -142,17 +142,20 @@
<literal>&lt;http></literal> element is the parent for all web-related namespace
functionality. The <literal>&lt;intercept-url></literal> element defines a
<literal>pattern</literal> which is matched against the URLs of incoming requests
using an ant path style syntax. You can also use regular-expression matching as an
alternative (see the namespace appendix for more details). The
<literal>access</literal> 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. The prefix <quote>ROLE_</quote> is a marker which indicates that a
simple comparison with the user's authorities should be made. In other words, a
normal role-based check should be used. Access-control in Spring Security is not
limited to the use of simple roles (hence the use of the prefix to differentiate
between different types of security attributes). We'll see later how the
interpretation can vary<footnote>
using an ant path style syntax<footnote>
<para>See the section on <link xlink:href="#request-matching">Request
Matching</link> in the Web Application Infrastructure chapter for more details
on how matches are actually performed.</para>
</footnote>. You can also use regular-expression matching as an alternative (see the
namespace appendix for more details). The <literal>access</literal> 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. The prefix
<quote>ROLE_</quote> is a marker which indicates that a simple comparison with the
user's authorities should be made. In other words, a normal role-based check should
be used. Access-control in Spring Security is not limited to the use of simple roles
(hence the use of the prefix to differentiate between different types of security
attributes). We'll see later how the interpretation can vary<footnote>
<para>The interpretation of the comma-separated values in the
<literal>access</literal> attribute depends on the implementation of the <link
xlink:href="#ns-access-manager">AccessDecisionManager</link> which is used. In

View File

@ -55,15 +55,16 @@
</section>
<section xml:id="filter-chain-proxy">
<title><classname>FilterChainProxy</classname></title>
<para> It should now be clear that you can declare each Spring Security filter bean that
you require in your application context file and add a corresponding
<para>Spring Security's web infrastructure should only be used by delegating to an
instance of <classname>FilterChainProxy</classname>. 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
<classname>DelegatingFilterProxy</classname> entry to <filename>web.xml</filename>
for each filter, making sure that they are ordered correctly. This is a cumbersome
approach and clutters up the <filename>web.xml</filename> file quickly if we have a
lot of filters. We would prefer to just add a single entry to
<filename>web.xml</filename> and deal entirely with the application context file for
managing our web security beans. This is where Spring Secuiryt's
<classname>FilterChainProxy</classname> comes in. It is wired using a
for each filter, making sure that they are ordered correctly, but this would be
cumbersome and would clutter up the <filename>web.xml</filename> file quickly if you
have a lot of filters. <classname>FilterChainProxy</classname> lets us add a single
entry to <filename>web.xml</filename> and deal entirely with the application context
file for managing our web security beans. It is wired using a
<literal>DelegatingFilterProxy</literal>, just like in the example above, but with
the <literal>filter-name</literal> set to the bean name
<quote>filterChainProxy</quote>. The filter chain is then declared in the
@ -89,8 +90,8 @@
context XML file in order to use this syntax.</para>
</footnote>. It maps a particular URL pattern to a chain of filters built up from
the bean names specified in the <literal>filters</literal> element. Both regular
expressions and Ant Paths are supported, and the most specific URIs appear first. At
runtime the <classname>FilterChainProxy</classname> will locate the first URI
expressions and Ant Paths are supported, and the most specific URLs appear first. At
runtime the <classname>FilterChainProxy</classname> will locate the first URL
pattern that matches the current web request and the list of filter beans specified
by the <literal>filters</literal> attribute will be applied to that request. The
filters will be invoked in the order they are defined, so you have complete control
@ -106,18 +107,10 @@
<classname>SecurityContextPersistenceFilter</classname> (with its default
<literal>allowSessionCreation</literal> as <literal>true</literal>) would likely be
sufficient.</para>
<para>In relation to lifecycle issues, the <classname>FilterChainProxy</classname> will
always delegate <methodname>init(FilterConfig)</methodname> and
<methodname>destroy()</methodname> methods through to the underlaying
<interfacename>Filter</interfacename>s if such methods are called against
<classname>FilterChainProxy</classname> itself. In this case,
<classname>FilterChainProxy</classname> guarantees to only initialize and destroy
each <literal>Filter</literal> bean once, no matter how many times it is declared in
the filter chain(s). You control the overall choice as to whether these methods are
called or not via the <literal>targetFilterLifecycle</literal> initialization
parameter of <literal>DelegatingFilterProxy</literal>. By default this property is
<literal>false</literal> and servlet container lifecycle invocations are not
delegated through <literal>DelegatingFilterProxy</literal>.</para>
<para>Note that <classname>FilterChainProxy</classname> 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.</para>
<para> When we looked at how to set up web security using <link
xlink:href="#namespace-auto-config">namespace configuration</link>, we used a
<literal>DelegatingFilterProxy</literal> with the name
@ -126,15 +119,15 @@
namespace. </para>
<section>
<title>Bypassing the Filter Chain</title>
<para> As with the namespace, you can use the attribute <literal>filters =
"none"</literal> 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 <classname>SecurityContext</classname> contents during a
request, then it must have passed through the security filter chain. Otherwise
the <classname>SecurityContextHolder</classname> will not have been populated
and the contents will be null.</para>
<para> You can use the attribute <literal>filters = "none"</literal> 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
<classname>SecurityContext</classname> contents during a request, then it must
have passed through the security filter chain. Otherwise the
<classname>SecurityContextHolder</classname> will not have been populated and
the contents will be null.</para>
</section>
</section>
<section>
@ -201,6 +194,78 @@
</listitem>
</orderedlist></para>
</section>
<section xml:id="request-matching">
<title>Request Matching and <interfacename>HttpFirewall</interfacename></title>
<para>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 <classname>FilterChainProxy</classname> decides which filter chain a
request should be passed through and also when the
<classname>FilterSecurityInterceptor</classname> 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.</para>
<para>The Servlet Specification defines several properties for the
<interfacename>HttpServletRequest</interfacename> which are accessible via getter
methods, and which we might want to match against. These are the
<literal>contextPath</literal>, <literal>servletPath</literal>,
<literal>pathInfo</literal> and <literal>queryString</literal>. Spring Security is
only interested in securing paths within the application, so the
<literal>contextPath</literal> is ignored. Unfortunately, the servlet spec does not
define exactly what the values of <literal>servletPath</literal> and
<literal>pathInfo</literal> will contain for a particular request URI. For example,
each path segment of a URL may contain parameters, as defined in <link
xlink:href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</link><footnote>
<para>You have probably seen this when a browser doesn't support cookies and the
<literal>jsessionid</literal> 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</para>
</footnote>. The Specification does not clearly state whether these should be
included in the <literal>servletPath</literal> and <literal>pathInfo</literal>
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>
<para>The original values will be returned once the request leaves the
<classname>FilterChainProxy</classname>, so will still be available to the
application.</para>
</footnote>. Other variations in the incoming URL are also possible. For example, it
could contain path-traversal sequences (like <literal>/../</literal>) or multiple
forward slashes (<literal>//</literal>) 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,
<classname>FilterChainProxy</classname> uses an
<interfacename>HttpFirewall</interfacename> 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>
<para>So, for example, an original request path
<literal>/secure;hack=1/somefile.html;hack=2</literal> will be returned as
<literal>/secure/somefile.html</literal>.</para>
</footnote>. It is therefore essential that a
<classname>FilterChainProxy</classname> is used to manage the security filter chain.
Note that the <literal>servletPath</literal> and <literal>pathInfo</literal> 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. </para>
<para>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 <classname>AntPathRequestMatcher</classname> which uses Spring's
<classname>AntPathMatcher</classname> to perform a case-insensitive match of the
pattern against the concatenated <literal>servletPath</literal> and
<literal>pathInfo</literal>, ignoring the <literal>queryString</literal>.</para>
<para>If for some reason, you need a more powerful matching strategy, you can use
regular expressions. The strategy implementation is then
<classname>RegexRequestMatcher</classname>. See the Javadoc for this class for more
information.</para>
<para>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
<quote>deny-by-default</quote> approach where you have a catch-all wildcard
(<literal>/**</literal>) defined last and denying access.</para>
<para>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.</para>
</section>
<section>
<title>Use with other Filter-Based Frameworks</title>
<para>If you're using some other framework that is also filter-based, then you need to