Why to invalidate a session in a security filter, CSRF token is not updated - spring

In a Spring Security application, a user is making a GET call to a private /secure url with activated csrf.
The user sends a credential cookie and then the user is authenticated and JSESSIONID is generated.
In the same browser becomes to make a GET call to /secure, with the JSESSIONID cookie and a credential cookie by another user.
In this case an owner security filter produces a change of user, invalid session of the first user, generate a
new session and authenticates the new credential.
This works until the new authenticated user sends a POST with the new JSESSIONID, but the CSRF token is getting to the old.
CsrfFilter is before my authentication filter, and CsrfAuthenticationStrategy not regenerate the CSRF token.
What is the right way to invalidate a session and the token csrf be updated?

The invalidation of session in spring security is automatic once you relogin as new user using the old JSESSION ID.
Basically you just have to bind the token generated on the new session generated by the authentication filter.
Try this solution, another filter after authentication
https://github.com/aditzel/spring-security-csrf-filter/blob/master/src/main/java/com/allanditzel/springframework/security/web/csrf/CsrfTokenResponseHeaderBindingFilter.java
Same issue reported via this thread,
https://github.com/aditzel/spring-security-csrf-token-interceptor/issues/1

I created a HttpSessionCsrfTokenRepository bean type, which is the default implementation of CsrfTokenRepository, and use both to configure spring security repository csrf as my filter, so the two sites use the same bean.
Config Bean
#Bean
public void CsrfTokenRepository dafaultCsrfTokenRepository() {
return new HttpSessionCsrfTokenRepository();
}
Spring Security
#Autowired
private CsrfTokenRepository csrfTokenRepository;
private static HttpSecurity addCSRF(HttpSecurity http, CsrfTokenRepository csrfTokenRepository) throws Exception {
http.csrf().csrfTokenRepository(csrfTokenRepository);
return http;
}
Invalidate session in filter
private void invalidateCurrentSession(HttpServletRequest request, HttpServletResponse response) {
SecurityContextHolder.clearContext();
HttpSession session = request.getSession(false);
if (session != null) {
CsrfToken csrfToken=csrfTokenRepository.loadToken(request);
session.invalidate();
request.getSession();
csrfTokenRepository.saveToken(csrfToken, request, response);
}
}
If someone wanted to override the default behavior, you could define a bean that implements CsrfTokenRepository as #Primary

Related

Is it good idea to take user id from SecurityContextholder in spring boot RestController..?

I am developing spring boot rest application for an ecommerce app, suppose i have endpoint /shipping-address which will fetch all the saved addresses for the user, is it good idea to take user id from SecurityContextHolder like
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Long userId;
if (principal instanceof UserPrincipal) {
userId = ((UserPrincipal) principal).getId();
}
or should i pass from the client side in the request body..? which is correct..? If i take from SecurityContextHolder is it problem when it comes to Horizontal scaling..??
Please help, i am new to backend development. Thanks.
Taking the userId from the SecurityContext is a good idea because it will prevent from hacking your application.
If you pass the userId from the client somebody could intercept the request and change the userId.
In regards to scaling it depends how you authenticate the user. If it's basic or token based and does not rely on session information. Everything will be fine.
SecurityContext
There is no problem in using a SecurityContext with Spring MVC.
You could use something like :
#RestController
#RequestMapping(path = "/auth")
#Slf4j
public class AuthResource {
#GetMapping(path = "whoami", produces = MediaType.APPLICATION_JSON_VALUE)
#PreAuthorize("isAuthenticated()")
public ResponseEntity<String> whoami(#AuthenticationPrincipal() UserDetails userDetails) {
String login = userDetails.getUsername();
return ResponseEntity.ok(login);
}
}
The annotation #AuthenticationPrincipal from Spring Security will simplify your code.
Session storage
By default, the session will be backed by the HttpSession (cookie JSESSIONID).
So you'll need something like sticky sessions if using a load balancer.
However, sessions can be stored elsewhere, like in relational databases (JDBC) or in Redis : this is what Spring Session can do.
See also Control the Session with Spring Security.
You can also choose to not use sessions with Spring Security :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

Spring Boot - JWT authentication without db calls

Is it possible to implement simple JWT authentication (not caring about invalidating tokens - I'll do it in cache) without database calls to load user into Security Context? I see my current implementation hits database with every call to api (to load user into security context). Below you can see part of implementation of JwtAuthenticationFilter extending OncePerRequestFilter:
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Long userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
And here is the call to database, which I would like to avoid (with every authenticated call to api):
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
UserRepository userRepository;
// This method is used by JWTAuthenticationFilter
#Transactional
public UserDetails loadUserById(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new UsernameNotFoundException("User not found with id : " + id)
);
return UserPrincipal.create(user);
}
}
I found some kind of solution of problem to build UserPrincipal object (it implements UserDetails interface) with only user id, username and granted authorities, and without e.g. password, which I cannot read from JWT token itself (the rest I can), but I am not sure if it's secure and and considered as a good-practice solution (UserDetails class requires password field and storing it JWT would not be wise I think). I need UserPrincipal instance (implementing UserDetails interface) to support as argument to UsernamePasswordAuthenticationToken, as you can see in the first paragraph.
One approach can be having two filter as follows.
CustomAuthenticationFilter that serves only login request/endpoint. You can do the following in this filter,
Call the db and validate the credential and retrieve the roles of the user
Generate the JWT token and you can store the user id or email along with roles in the subject of the JWT token. As we are adding user specific details I would recommend to encrypt the JWT token.
CustomAuthroizationFilter that serves all other requests/endpoints. You can do the following in this filter,
Validate JWT token
Retrieve the user id or email along with roles of the user from the subject of the JWT token.
Build spring authentication (UsernamePasswordAuthenticationToken) and set it in SecurityContextHolder like you did.
This way you will be calling db only during the login request not for all other api endpoints.

is there any way to "reset" the csrf token after successful login?

I just enabled the csrf token protection on my spring security (with xml) by adding the csrf tag.
It works great but I noticed when I hit the login form I am sending a csrf token, after a successful login the csrf token is still the same, is there anyway I can reset it after hit the login form?
After checking the docs I found no clue how to do this or if is due my repository? (Im not using any custom csrf repository)
Ideas?
The official documentation states that the CSRF token is stored in the HttpSession by default. Since the token is stored in the session, it will continue to get reused after the user has logged in.
If you really want to reset the token on a successful login, you will have to handle successful login events.
public class AuthenticationSuccessCsrfTokenResetHandler
extends SimpleUrlAuthenticationSuccessHandler {
private final CsrfTokenRepository repository;
public AuthenticationSuccessCsrfTokenResetHandler(final CsrfTokenRepository repository) {
this.repository = repository;
}
#Override
public void onAuthenticationSuccess(final HttpServletRequest request
, final HttpServletResponse response
, final Authentication authentication)
throws ServletException, IOException {
repository.saveToken(repository.generateToken(request), request, response)
}
}
Then, configure this handler in your Spring Security configuration.

How to have Spring Security enabled for an application using third party login?

I have a Spring Boot enabled application whose login is controlled by third party Siteminder application. After successful authentication, Sitemeinder redirects to our application url. We fetch the HttpRequest from Siteminder and process the requests.
Now, how can Spring security be enabled in this case for authorizing users based on roles.
#Controller
public class LoginController
#RequestMapping( value= "/")
public void requestProcessor(HttpServletRequest request)
{
.
.
.}
The above controller's request mapper reads the request coming from SiteMinder and processes the request which has the Role of the user logged in. Where can we have Spring Security enabled to authorize pages and service methods to the user.
This is an scenario for the PreAuthenticated security classes:
Take a look here:
http://docs.spring.io/spring-security/site/docs/current/reference/html/preauth.html
Spring Security processes request before it gets to your controller in a filter configured in spring security configuration.
There is a documentation on how to configure spring security with SiteMinder.
The rules in your configuration will define the access to resources
Depends what you get in session. If somehow u can to take user and password from session you can authenticate user directly from code as :
#Autowired
AuthenticationManager authenticationManager;
...
public boolean autoLogin(String username, String password) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
Authentication auth = authenticationManager.authenticate(token);
if (auth.isAuthenticated()) {
logger.debug("Succeed to auth user: " + username);
SecurityContextHolder.getContext().setAuthentication(auth);
return true;
}
return false;
}

spring oauth2 how to get a new refresh token every time

When I call refresh token the web service returns a new access token and the old refresh token because the access token has not yet expired. Is there any configuration that when I call refresh token this web service return me a new access token and a new refresh token?
Spring can be configured not to reuse refresh tokens - by default it reuses -, this should solve the issue.
#Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
configurer.reuseRefreshTokens(false);
}
}
You can use AOP to invalidate token by removing old one, or change its validity time, before generate new one,but if you do so then you are going to invalidate all refresh tokens used by other clients.

Resources