Spring Boot 2.3.4: Bug with JwtValidators.createDefaultWithIssuer(String)? - spring-boot

I found an odd behavior with JWT parsing and JwtValidators.
Scenario:
Spring Boot OIDC client (for now a tiny web app, only displaying logged in user and some OIDC objects provided by Spring)
Custom JwtDecoderFacotry<ClientRegistration> for ID-Token validation
JwtValidatorFactory based on JwtValidators.createDefaultWithIssuer(String)
This worked well with Spring Boot version <= 2.2.10.
Debugging:
NimbusJwtDecoder (JAR spring-security-oauth2-jose) uses claim set converters. The 'iss' (issuer) claim is handled as URL.
JwtIssuerValidator (internally created by JwtValidators.createDefaultWithIssuer(String)) wraps a JwtClaimValidator<String>.
this one finally calls equals() that is always false - it compares String with URL.
My current workaround is not calling JwtValidators.createDefaultWithIssuer() but just using the validators new JwtTimestampValidator() and an own implementation of OAuth2TokenValidator<Jwt> (with wrapping JwtClaimValidator<URL>).
Anyone else having trouble with this?
--Christian

It's a bug. Pull Request is created.

Related

CSRF protection not working with Spring Security 6

I upgraded my project to Spring Boot 3 and Spring Security 6, but since the upgrade the CSRF protection is no longer working.
I'm using the following configuration:
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated())
.httpBasic(withDefaults())
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS))
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
.build();
}
#Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder().username("user").password("{noop}test").authorities("user").build();
return new InMemoryUserDetailsManager(user);
}
On my webpage I only have a single button:
<button id="test">Test CSRF</button>
And the following JavaScript code:
document.querySelector("#test").addEventListener('click', async function() {
console.log('Clicked');
// This code reads the cookie from the browser
// Source: https://stackoverflow.com/a/25490531
const csrfToken = document.cookie.match('(^|;)\\s*XSRF-TOKEN\\s*=\\s*([^;]+)')?.pop();
const result = await fetch('./api/foo', {
method: 'POST',
headers: {
'X-XSRF-Token': csrfToken
}
});
console.log(result);
});
In Spring Boot 2.7.x this setup works fine, but if I upgrade my project to Spring Boot 3 and Spring Security 6, I get a 403 error with the following debug logs:
15:10:51.858 D o.s.security.web.csrf.CsrfFilter: Invalid CSRF token found for http://localhost:8080/api/foo
15:10:51.859 D o.s.s.w.access.AccessDeniedHandlerImpl: Responding with 403 status code
My guess is that this is related to the changes for #4001. However I don't understand what I have to change to my code or if I have to XOR something.
I did check if it was due to the new deferred loading of the CSRF token, but even if I click the button a second time (and verifying that the XSRF-TOKEN cookie is set), it still doesn't work.
I have recently added a section to the reference documentation for migrating to 5.8 (in preparation to 6.0) that demonstrates a solution for this issue.
TL;DR See I am using AngularJS or another Javascript framework.
The issue here is that AngularJS (and your example code above) are using the XSRF-TOKEN cookie directly. Prior to Spring Security 6, this was fine. But unfortunately, the cookie is actually used to persist the raw token, and with Spring Security 6, the raw token is not accepted by default. Ideally, front-end frameworks would be able to use another source to get the token, such as an X-XSRF-TOKEN response header.
However, even with Spring Security 6, such a response header is not provided out of the box, though it could be a possible enhancement worth suggesting. I have not yet suggested such an enhancement since Javascript frameworks would not be able to use it by default.
For now, you will need to work around the problem by configuring Spring Security 6 to accept raw tokens, as suggested in the section I linked above. The suggestion allows raw tokens to be submitted, but continues to use the XorCsrfTokenRequestAttributeHandler to make available the hashed version of the request attribute (e.g. request.getAttribute(CsrfToken.class.getName()) or request.getAttribute("_csrf")), in case anything renders the CSRF token to an HTML response which could be vulnerable to BREACH.
I would recommend finding a reputable source for researching BREACH more thoroughly, but unfortunately I cannot claim to be such a source.
I would also recommend keeping an eye on Spring Security issues for now, as things may change quickly once the community begins consuming Spring Security 6. You can use this filter as a possible way to keep track of CSRF-related issues.
We have an angular angular application with spring-boot. We tried to migrate to spring-boot 3 (Spring Security 6). And we faced the same problem.
We tried many methods including some of the solutions from this question's answer but we failed. After spending time we found the solution from the spring security doc.
What we need to do is, set the CsrfRequestAttributeName to null in the configuration.
requestHandler.setCsrfRequestAttributeName(null);
What actually happened:
The CsrfToken will be loaded on each request in Spring Security version 5 by default. This means that in a typical setup, every request—even those that are not necessary—must have the HttpSession read.
The default behavior of Spring Security 6 is to postpone looking up the CsrfToken until it is required.
Our application needs the token every time. So, We need to opt into the 5.8 defaults.
The example code is given below (from doc):
#Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName(null);
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
Thanks for this! I was able to use it to solve a similar project in a JHipster + Spring Boot 3 app. However, it seems the class name might've changed recently. Here's what I had to use:
.csrf(csrf -> csrf
.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(new ServerCsrfTokenRequestAttributeHandler()))
I currently worked around the problem by disabling the XorCsrfTokenRequestAttributeHandler like this:
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// Added this:
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()))
However, this means that I'm likely vulnerable against the BREACH attack.
Using the accepted answer breaks tests that require CSRF using Spring Security's SecurityMockMvcRequestPostProcessors.crsf() I can either only use CsrfTokenRequestAttributeHandler, or XorCsrfTokenRequestAttributeHandler in Spring Boot's CSRF configuration, both give positive test results.
Using the accepted answer makes Angular work but breaks tests.
So the only workaround at the moment seems to be using CsrfTokenRequestAttributeHandler and so effectively disabling Spring Security's BREACH-protection.
As of Spring Security 6.0.1 and Spring Boot 3.0.2, following the instructions from the accepted answer fails on the first request but succeeds thereafter. The reason it fails on the first request is because the token's cookie never gets created until a protected method is invoked. This is because the method CookieCsrfTokenRepository.saveToken only gets called when the CsrfFilter calls deferredCsrfToken.get(), which only gets called on POST, PUT, PATCH, and DELETE methods. Unfortunately, under the current implementation, that means the client has to expect a failure on the first request. Under previous versions of Spring Security, we used to be able to count on the token's cookie being included in the response to GET, HEAD, or OPTIONS requests.

Is it a good idea to handle optional JWT Authentication in Filter?

I am new to Spring Boot and my current project is a REST API developed in Spring Webflux. The goal is to have an endpoint which has an optional JWT Token, allowing you ti create things anonymously or not. But all the starter guides to Spring Security are really complicated and use Spring MVC, as far as I can tell.
Now my idea was to create a HandlerFilterFunction looking like
class AuthenticationFilter : HandlerFilterFunction<ServerResponse, ServerResponse> {
override fun filter(request: ServerRequest, next: HandlerFunction<ServerResponse>): Mono<ServerResponse> {
val authHeader = request.headers().header("Authorization").firstOrNull()
// get user from database
request.attributes()["user"] = user
return next.handle(request)
}
}
and adding it to the router {...} bean.
Is this a good idea, or should I go another router? If so, can somebody point me towards a JWT tutorial for Spring Webflux.
The Spring Security docs point to a JWT-Based WebFlux Resource Server sample in the codebase.
It's not Kotlin-based, so I also posted a sample of my own just now; hopefully, it helps get you started.
As for your question, yes, you can create a filter of your own, though Spring Security ships with a BearerTokenAuthenticationFilter that already does what your filter would likely do. The first linked sample adds this filter manually while the second sample lets Spring Boot add it.

How to use Apache Shiro for authorization only?

Before I explain the issue I should say that we only need Apache Shiro for authorization and athentication is already enabled with OAuth2.
So my code to enable Shiro is exactly as the code in this link here.
I have also checked this issue. But for me if I enable LifecycleBeanPostProcessor almost most beans will be null. I made that create method in config class static as it suggests in the second link but no luck.
So my question is, is there any way to only enable authorization without registering shiro filter? If not, how to get around this issue? Because it seems ShiroFilterFactoryBean requires LifecycleBeanPostProcessor and that breaks the whole application.
We are using latest version of Spring Boot and Shiro 1.2.4
As outlined in an issue in the comments, you would need to set an already authenticated identity in the subject, which can be done with the Subject.Builder() (I'm using version 1.5.2 here).
Subject user = new Subject.Builder()
.principals(new SimplePrincipalCollection("bud", "myRealm"))
.authenticated(true)
.buildSubject();
if (user.hasRole("admin")) {
// do some authorized stuff
}
When implementing a custom realm the authentication ability can be disabled by returning false from the Realm’s supports method as described here .

Spring security - implement oauth with existing FilterChainProxy

we have existing web application built with Spring security 3.1 ,Wink(for rest)
we now need to add oauth2 (client_credentials flow) for several resources, i looked into many examples and all of them using the Http namespace configuration along with spring dispatcher servlet (which we didn't have till now)
problem is that http namespace is creating a springSecurityFilterChain which we already had in the application , so first thing i renamed the existing filter so the default could co-exist with the old one.
but this does not work, its either the existing chain working for requests or the new one.
i have tried the following already
1. disabled dispatcher servlet context by giving empty config location (in web.xml)
2. tried to have the oauth configuration in application-context.xml (right to the existing FilterChainProxy)
3. Allow the /oauth/token in existing chain by setting its filter to none (so the new can take over)
4. tried to declare the oauth filters in the existing chain but there was a problem with its not getting the right clientAuthentication
i really don't know what else to try - so the question is : is it possible to have both declared in the same webapp ? or is it possible to declare oauth2 configuration in the old fashion.
thanks
Shlomi
I managed to do that eventually, having the API (protected with oauth) completey separated url from the rest of the application.
so the Http namespace is creating the springSecurityFilterChain bean and the others just have different bean names. everyone is delegated through the DelegatingProxy in web.xml
i needed to puth the API URL prefix in other chains and allow all requests through , leaving the oauth security chanin to deal with security.
(i.e filter-chain pattern="/api/**" filters="none)
regarding the spring oauth2 bounded to spring MVC so tight i think is not a good implementation.
the mapping of the dispatcher servlet cannot be for /* but have to be something like /auth/*
so a special filter inherit from ClientCredentialsTokenEndpointFilter with special path like super("/auth/oauth/token") was needed.
it also cannot be /api/* since this is the real API URI mapped by our rest framework (wink RestServlet)
so we have something like this
http://server:port/context/auth/oauth/token
http://server:port/context/api/someresource (protected with oauth2)
http://server:port/context/rest/someresource (old rest for application)
Shlomi

spring-security-redirect is not read by spring security 3.1?

So we're using spring-security-redirect as a parameter in the form that is sent to j_spring_security_check, in order to send the user to the correct page after a successful login. Migrating from Spring security 3.0 to 3.1, this stopped working. We use a subclass of SavedRequestAwareAuthenticationSuccessHandler, overriding onAuthenticationSuccess(), and debugging that method I see that getTargetUrlParameter() returns null. isAlwaysUseDefaultTargetUrl() returns false.
Browsing around I can't find anyone having similar problems... I find some references to AbstractAuthenticationTargetUrlRequestHandler.DEFAULT_TARGET_PARAMETER, which seems to have disappeared in 3.1.
Any ideas?
As per Spring security 3.1 xsd,
Attribute : authentication-success-handler-ref
Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication
request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the
implementation should always deal with navigation to the subsequent destination.
So, in your subclass, you have to perform the redirection.

Resources