I'm using Spring 4.3.12.RELEASE Version, AngularJS 1.4.8. I'm trying to prevent the CSRF Attack on the application.
@Configuration
@Order(2)
public static class SecurityConfig extends WebSecurityConfigurerAdapter {
String[] pathsToRemoveAuthorizaton = {
"/mobile/**",
"/logout",
"/j_spring_security_logout",
"/login",
};
private final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
@Override
public void configure(HttpSecurity http) throws Exception {
logger.info("http configure");
http.antMatcher("/**").authorizeRequests().antMatchers(pathsToRemoveAuthorizaton).permitAll()
.antMatchers("/**").authenticated()
.and().formLogin().loginPage("/login")
.usernameParameter("employeeId").passwordParameter("password")
.successForwardUrl("/dashboard").defaultSuccessUrl("/dashboard", true)
.successHandler(customAuthenticationSuccessHandler()).loginProcessingUrl("/j_spring_security_check")
.and().logout().logoutSuccessUrl("/logout").logoutUrl("/j_spring_security_logout")
.logoutSuccessHandler(customLogoutSuccessHandler()).permitAll().invalidateHttpSession(true)
.deleteCookies("JSESSIONID").and().sessionManagement().sessionFixation().newSession()
.maximumSessions(1).maxSessionsPreventsLogin(true).and()
.sessionCreationPolicy(SessionCreationPolicy.NEVER).invalidSessionUrl("/logout").and()
.exceptionHandling().accessDeniedPage("/logout");
// http.csrf().csrfTokenRepository(csrfTokenRepository()).and()
// .addFilterAfter(new StatelessCSRFFilter(), CsrfFilter.class);
http.csrf().csrfTokenRepository(csrfTokenRepository());
// http.csrf().disable();
// http.csrf().ignoringAntMatchers("/mobile/**");
http.authorizeRequests().anyRequest().authenticated();
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
@Bean
public AuthenticationSuccessHandler customAuthenticationSuccessHandler() {
return new CustomAuthenticationSuccessHandler();
}
@Bean
public LogoutSuccessHandler customLogoutSuccessHandler() {
return new CustomLogoutSuccessHandler();
}
}
Below is my angular service code
govtPMS.service('Interceptor', function($q, $location, $rootScope, pinesNotifications, Auth) {
return {
request: function(config) {
config.headers.Authorization = 'Bearer '+$rootScope.authToken;
// document.cookie = 'CSRF-TOKEN=' + $rootScope.generateKey;
return config;
},
requestError: function (rejection) {
return $q.reject(rejection);
},
response: function(res) {
if(res.status === 200 || res.status === 201){
if(res.data.response !== undefined){
if(res.data.status === 1 || res.data.status === 3 || res.data.status === 2) {
pinesNotifications.notify({
'title': 'Success',
'text': res.data.message,
'type': 'success',
'delay': 5000
});
}
else if(res.data.status === 5) {
pinesNotifications.notify({
'title': 'Warning',
'text': res.data.message,
'type': 'warning',
'delay': 5000
});
}
}
}
return res || $q.when(res);
},
responseError: function(error) {
return $q.reject(error);
}
};
}).config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.xsrfHeaderName = 'X-CSRF-TOKEN';
$httpProvider.defaults.xsrfCookieName = 'CSRF-TOKEN';
$httpProvider.interceptors.push('Interceptor');
}])
I'm still not able to see the CSRF Token header along with requests.
In this application, we are using 3 jsp pages - login.jsp, logout.jsp and dashboard.jsp angular scope is defined in the dashboard.jsp, hence login and logout are out of scope of AngularJS. I've also tried it the stateless way from this and this examples, where angular is generating a UUID and appending with cookie and request header, the below filter was doing the job fine. Until the logout attack. In this attack, the attacker is trying to succesfully logout the user since to logout from the application, we are simply using a href.
<li><a href="j_spring_security_logout" ><i class="fa fa-sign-out"></i><span>Logout</span></a></li>
Now since its logout is out of angular, the angularjs interceptor is cannot attach the UUID there. I've been struggling on this since past week, Any help will be appreciated.
StatelessCSRFFilter.java
package com.leadwinner.sms.config.filters;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.web.filter.OncePerRequestFilter;
public class StatelessCSRFFilter extends OncePerRequestFilter {
private static final String CSRF_TOKEN = "CSRF-TOKEN";
private static final String X_CSRF_TOKEN = "X-CSRF-TOKEN";
private final AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
List<String> excludedUrls = new ArrayList<>();
excludedUrls.add("/resources");
excludedUrls.add("/j_spring_security_check");
excludedUrls.add("/j_spring_security_logout");
excludedUrls.add("/login");
excludedUrls.add("/logout");
excludedUrls.add("/mobile");
excludedUrls.add("/migrate");
excludedUrls.add("/dashboard");
String path = request.getServletPath();
System.out.println(path);
AtomicBoolean ignoreUrl = new AtomicBoolean(false);
excludedUrls.forEach(url -> {
if (request.getServletPath().startsWith(url.toLowerCase()) || request.getServletPath().equals("/")) {
ignoreUrl.set(true);
}
});
if (!ignoreUrl.get()) {
final String csrfTokenValue = request.getHeader(X_CSRF_TOKEN);
final Cookie[] cookies = request.getCookies();
System.out.println("**************************************************");
System.out.println("--------------------------------------------------");
String csrfCookieValue = null;
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(CSRF_TOKEN)) {
csrfCookieValue = cookie.getValue();
}
}
}
System.out.println("csrfTokenValue = "+csrfTokenValue);
System.out.println("csrfCookieValue = "+csrfCookieValue);
System.out.println("--------------------------------------------------");
System.out.println("**************************************************");
if (csrfTokenValue == null || !csrfTokenValue.equals(csrfCookieValue)) {
accessDeniedHandler.handle(request, response, new AccessDeniedException(
"Missing or non-matching CSRF-token"));
return;
}
}
filterChain.doFilter(request, response);
}
}