-
Notifications
You must be signed in to change notification settings - Fork 6k
WebInvocationPrivilegeEvaluator
Bean should support multiple SecurityFilterChain
s
#10554
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
WebInvocationPrivilegeEvaluator
Bean should support multiple SecurityFilterChain
s
Hi @fast-reflexes, thanks so much for the detailed response. The |
Could you elaborate on this :) When you say that the API works as intended, do you mean:
No matter which, I think it's odd for ANY use case that I'm a bit puzzled as to how you can not see this as a bug... all in all, you think that the current functionality is correct? Could you elaborate on what grounds you think it should deny access? |
Please ignore the line about the API. I mean that that implementation is behaving as intended since it was designed for a single filter chain. We are in talks with the Spring Boot team and will come up with a solution that works for everyone. |
@marcusdacoregio great! Thanks! I'd love to check it out but I don't understand how to get that version.. can't find it in Maven repo nor JCenter, can't find any branch in your repo called that.. I tried to add it as constraints in my Gradle file but to not avail.. could you give me a hint? :) |
@fast-reflexes Thanks for logging the issues, I stumbled across the same problem today and it was a relief to come find your detailed explanations. I would have expected the snapshot builds to be in here, but the changes aren't in the most recent build The Maven config for the snapshot repo is:
with extra property: @marcusdacoregio I think this has to be included in 5.6.x as it's very much broken when used with Spring Boot 2.6.x |
Thanks a lot @darrenpm ! SInce I'm using Gradle, I had to adapt it a bit:
... then ...
After this, when I run
Version Nonetheless, the problems are not solved, neither for my private projects, nor the one in the repo I supplied. I don't know if I'm doing something wrong though :( Here is an updated branch which still fails using the |
Yeah I can confirm that build doesn't fix the problem for me. |
@fast-reflexes can you test with |
@darrenpm Seems it requires Java 17 and we have used Java 11 before .. and the transition does not seem trivial so I have to come back later on that ;) |
Yeah, same for me. I think this needs to be backported to 5.6 |
Hey everyone. Thanks for trying it. The changes are not in @darrenpm thank you for helping @fast-reflexes on how to configure a different version for Spring Security.
I do share the same thought. @rwinch is on PTO for now, but I'll talk to him to see if we can backport this to be included in the next |
@marcusdacoregio I'm glad to say that everything works as expected with Nonetheless, there is another problem which has surfaced now, it seems that when the
...when it tries to access the remote address on the request. The odd thing is that the checks from inside the security chain itself works out without exceptions so seems to be some other problem (probably unrelated to the problem of this ticket) related to this new filter (and possible some of the Spring Security classes it uses). I'll see if I have the time to put together a new minimal demo showing this. Thanks again all! ➕ |
Thanks @fast-reflexes. That |
Great @marcusdacoregio ! I filed one: #10664 :) |
Hey folks, just a heads up that the fix has been backported to Stay tuned and, if possible, try the versions as soon as possible. |
Background
Having multiple
WebSecurityConfigurerAdapter
's which are ordered and each processes a limited set of paths is a very handy tool for creating a structured security configuration. The advantages are:We have had a setup with multiple adapters for a long time and it has worked very well. Spring does not disallow it and there are parts in the code which even suggests that this is an intended use (for example the property
securityFilterChainBuilders
in singletonWebSecurity
which is aList
of builders and not a single object). The role of methodHttpSecurity.requestMatchers
also encourages such use.Bug
Nonetheless, parts of Spring Security does not take this into account. More specifically, the
WebSecurity
singleton has a property namedfilterSecurityInterceptor
which is populated with theFilterSecurityInterceptor
from the LASTWebSecurityConfigurerAdapter
processed. ThisFilterSecurityInterceptor
is then added to the beanDefaultWebInvocationPrivilegeEvaluator
created via bean methodprivilegeEvaluator
inWebSecurityConfiguration
. This means that any use of propertyfilterSecurityInterceptor
in singletonWebSecurity
, or any use of beanDefaultWebInvocationPrivilegeEvaluator
will only take the last processedWebSecurityConfigurerAdapter
into account, which does not seem correct. Luckily, it seems to me that the only use of propertyfilterSecurityInterceptor
is to create beanDefaultWebInvocationPrivilegeAdapter
and the only use of this bean is in newly added filterErrorPageSecurityFilter
, which is also where we can see this bug at play. Nonetheless, the design seems flawed.Note that this bug is NOT any of the following:
ErrorPageSecurityFilter
inherits fromHttpFilter
and notFilter
causingClassDefNotFound
exception in some environments:MockMvc
does not excludeErrorPageSecurityFilter
, which should only be included forERROR
dispatches, from regular dispatches. This is due toMockFilterChain
mistakenly including this filter no matter the dispatch type whereas the realApplicationFilterChain
used by Spring will exclude it if the dispatch type is notERROR
:... however, in the case of the problems with
MockMvc
, the bug presented herein come into play as well (even though there is also a problem withMockMvc
and theirMockFilterChain
not taking dispatcher type into account).Reproduce
Reproduction of bug is shown with filter
ErrorPageSecurityFilter
.Download project https://github.com/fast-reflexes/spring-boot-bug/tree/filterSecurityInterceptor
Project involves a security setup like
See bug in action
Start app with
./gradlew bootRun
Go to
http://localhost:8080/non-existing
. This endpoint is restricted so Spring will want to send an error with 403. A new error dispatch starts.Check console and verify that Spring Security filter chain accepts the request, but when the
ErrorPageSecurityFilter
processes it, the request is considered unauthorized, because theDefaultWebInvocationPrivilegeEvaluator
only considers the lastWebSecurityConfigurerAdapter
which is a catch-all that denies everything. Therefore, only a status code of 403 is sent and no error page. Nonetheless, the firstWebSecurityConfigurerAdapter
explicitly allows access to/error
to anyone:Fix bug by changing last
WebSecurityConfigurerAdapter
to permit allChange the last configuration to
permitAll()
instead ofdenyAll()
.Run the previous test again.
Verify that the an actual error page is sent and output in console shows that access to
/error
is now permitted by theDefaultWebInvocationPrivilegeEvaluator
:Chain of events
springFilterSecurityChain
is executed in fileWebSecurityConfiguration
. This method creates the Spring Security filter chain filter.WebSecurityConfigurerAdapter
given, itsinit
method is executed. TheHttpSecurity
object is fetched for each config and queued as a builder insecurityFilterChainBuilders
property of typeList
inWebSecurity
via methodaddSecurityFilterChainBuilder
. This method returns theWebSecurity
object itself. In the sameinit
call, theWebSecurity.postBuildAction
property is set to aRunnable
which adds theFilterSecurityInterceptor
of the current config as thefilterSecurityInterceptor
property of theWebSecurity
singleton itself. Since this method call sets propertypostBuildAction
inWebSecurity
, it overwrites the previousRunnable
that this property was set to earlier. ThepostBuildAction
Runnable
is not executed yet but the final assigned property is aRunnable
which involves theHttpSecurity
object of the lastWebSecurityConfigurerAdapter
processed.WebSecurity
List
propertysecurityFilterChainBuilders
with builders (and thepostBuildAction
is overwritten each time), each config is processed and built. In methodconfigure
ofAbstractInterceptUrlConfigurer
, aFilterSecurityInterceptor
is created for and attached to each configuration.postBuildAction
inWebSecurity
is executed and attaches theFilterSecurityInterceptor
of the last processed config as thefilterSecurityInterceptor
of the singletonWebSecurity
object. TheFilterSecurityInterceptor
's of the other configs are, via the overwrittenpostBuildAction
Runnable
(and the fact that thesecurityInterceptor
of theWebSecurity
class is a single object and not a list of objects) ignored.WebSecurityConfiguration
, bean methodprivilegeEvaluator
is executed, constructing aWebInvocationPrivilegeEvaluator
bean in the shape of aDefaultWebInvocationPrivilegeEvaluator
which is constructed from methodgetPrivilegeEvaluator
in fileWebSecurity
. This method uses thefilterSecurityIinterceptor
set inWebSecurity
, which corresponds to theFilterSecurityInterceptor
of the last processedWebSecurityConfigurerAdapter
./error
. You will see a new invocation in Spring Security with this requested path, and since we have a config that matches and allows the/error
path, Spring Security will allow it. TheApplicationFilterChain
will then call the newErrorPageSecurityFilter
, because it is anERROR
dispatch.ErrorPageSecurityFilter,
the methoddoFilter
will execute, which will retrieve theWebInvocationPrivilegeEvaluator
bean, which is the bean from step 5. Once retrieved, this bean will use the available authentication along with itsFilterSecurityInterceptor
, from the last processedWebSecurityConfigurerAdapter
, to retrieve meta data security expressions and determine whether access can be given. If access is given, the initial dispatch is allowed, rendering a full error page. If access is denied, theErrorPageSecurityFilter
will send a response containing the intended http status code for the dispatch, but with nothing more./error
paths are allowed to everyone and with a last catch-all security config which denies all. In such a setup, the security filter chain will allow access to the error page, but theErrorPageSecurityFilter
will, erroneously, deny access to the error page even though the configuration indicates that it should be allowed.Problems
DefaultWebInvocationPrivilegeEvaluator
'sisAllowed
method should not be used since it doesn't make use of the full security config specified. Luckily, it seems this method is only used by theErrorPageSecurityFilter
.WebSecurity
's propertyfilterSecurityInterceptor
should not be used either since it ignores part of the security config. Seems it is only used to create theWebInvocationPrivilegeEvaluator
bean and since itsisAllowed
method is used only byErrorPageSecurityFilter
, its impact seems contained. Even if this problem is solved, I don't see how the concerned functionality of the given classes can continue to exist in their current form for any future use since they clearly don't capture the full given security config given to Spring.WebSecurityConfigurerAdapter
's and an exception should be thrown if multiple of these are present. Since I think multipleWebSecurityConfigurerAdapter
's have a real use-case, I suggest this bug should be fixed instead.The problem is not per se, very big in the prescribed case, but the logic is flawed and if more components use the
filterSecurityInterceptor
inWebSecurity
or theDefaultWebInvocationPrivilegeEvaluator
in the future, it could be worse. Therefore, it is important to either fix this bug or force users to only use oneWebSecurityConfigurerAdapter
(not recommended imho).Expected behaviour
Expected behaviour is that all use of property
filterSecurityInterceptor
inWebSecurity
singleton, and all use of beanDefaultWebInvocationPrivilegeEvaluator
should take the entire security config into account, not only the part coming from the last processedWebSecurityConfigurerAdapter
. Alternatively, Spring should reject multipleWebSecurityConfigurerAdapter
's. Since the latter seems counterintuitive and there is a good usecase for multipleWebSecurityConfigurerAdapter
s, I'd rather see this as a bug and that the relevant parts ofWebSecurity
andDefaultWebInvocationPrivilegeEvaluator
be rewritten.To conclude, in this specific case, the entire error page should be allowed by
ErrorPageSecurityFilter
in BOTH cases, even when the lastWebSecurityConfigurerAdapter
is a catch-all which denies all access simply because a previous one explicitly allowed access to the/error
path to everyone.A ticket has been filed in Spring Boot repo to to alert users of this behaviour in
ErrorPageSecurityFilter
: spring-projects/spring-boot#28818The text was updated successfully, but these errors were encountered: