Detailed explanation of using spring-security to solve CSRF problems

Introduction to CSRF

CSRF (Cross-site request forgery), Chinese name: Cross-site request forgery, also known as: one click attack/session riding, abbreviated as: CSRF/XSRF.

For the specific introduction and attack methods of SCRF, please refer to the introduction of Baidu Encyclopedia and the analysis of a big cow:
CSRF Baidu Encyclopedia talks
about CSRF attack methods Zhejiang Surrogacy

Configuration steps

1. Depend on jar package

  1. <properties>
  2. <spring.security.version>4.2.2.RELEASE</spring.security.version>
  3. </properties>
  4. <dependency>
  5. <groupId>org.springframework.security</groupId>
  6. <artifactId>spring-security-core</artifactId>
  7. <version>${spring.security.version}</version>
  8. </dependency>
  9. <dependency>
  10. <groupId>org.springframework.security</groupId>
  11. <artifactId>spring-security-web</artifactId>
  12. <version>${spring.security.version}</version>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.springframework.security</groupId>
  16. <artifactId>spring-security-config</artifactId>
  17. <version>${spring.security.version}</version>
  18. </dependency>
copy code

2.web.xml configuration

  1. <filter>
  2. <filter-name>springSecurityFilterChain</filter-name>
  3. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  4. </filter>
  5. <filter-mapping>
  6. <filter-name>springSecurityFilterChain</filter-name>
  7. <url-pattern>/*</url-pattern>
  8. </filter-mapping>
copy code

3. Spring configuration file configuration   Zhejiang surrogacy company

  1. <bean id="csrfSecurityRequestMatcher" class="com.xxx.CsrfSecurityRequestMatcher"></bean>
  2. <security:http auto-config="true" use-expressions="true">
  3. <security:headers>
  4. <security:frame-options disabled="true"/>
  5. </security:headers>
  6. <security:csrf request-matcher-ref="csrfSecurityRequestMatcher" />
  7. </security:http>
copy code

4. Custom RequestMatcher implementation class CsrfSecurityRequestMatcher

This class is used to customize which requests do not need to be intercepted and filtered. If csrf is configured, all http requests are intercepted by CsrfFilter, and there is a private class DefaultRequiresCsrfMatcher in CsrfFilter.

Source code 1: DefaultRequiresCsrfMatcher class

  1. private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
  2. private final HashSet<String> allowedMethods;
  3. private DefaultRequiresCsrfMatcher() {
  4. this.allowedMethods = new HashSet(Arrays.asList(new String[]{"GET", "HEAD", "TRACE", "OPTIONS"}));
  5. }
  6. public boolean matches(HttpServletRequest request) {
  7. return !this.allowedMethods.contains(request.getMethod());
  8. }
  9. }
copy code

From this source code, it can be found that the POST method is excluded, that is to say, only 4 types of methods such as GET|HEAD|TRACE|OPTIONS will be released, and the http requests of other methods must verify whether the token of _csrf is correct, and Usually, when the rest interface service is called in the post method, there is no token of _csrf, so our rest interface call will fail. We need to customize a class to release the type of interface. Take a look at our custom filter:

Source code 2: csrfSecurityRequestMatcher class

  1. public class CsrfSecurityRequestMatcher implements RequestMatcher {
  2. private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
  3. private RegexRequestMatcher unprotectedMatcher = new RegexRequestMatcher("^/rest/.*", null);
  4. @Override
  5. public boolean matches(HttpServletRequest request) {
  6. if(allowedMethods.matcher(request.getMethod()).matches()){
  7. return false;
  8. }
  9. return !unprotectedMatcher.matches(request);
  10. }
  11. }
copy code

Note: Generally, the rest interface services we define are brought with /rest/ , so if your project does not use this, or there is no rest service in the project, this class can be completely omitted.

5. post request configuration

Generally, there is a common jsp file in our project, which is referenced by every page, so we can do the following configuration in the common file:

  1. <meta name="_csrf" content="${_csrf.token}"/>
  2. <meta name="_csrf_header" content="${_csrf.headerName}"/>
  3. <script>
  4. var token = $("meta[name='_csrf']").attr("content");
  5. var header = $("meta[name='_csrf_header']").attr("content");
  6. $.ajaxSetup({
  7. beforeSend: function (xhr) {
  8. if(header && token ){
  9. xhr.setRequestHeader(header, token);
  10. }
  11. }}
  12. );
  13. </script>
copy code

The meaning of $.ajaxSetup is to add this header and token to all our requests, or put it in the form. Note that _csrf should match the configuration in the spring security configuration file, the default is _csrf.

Source code analysis

We know that since csrf is configured, all http requests will be intercepted by CsrfFilter, so look at the source code of CsrfFilter to see the principle at a glance. Here we only look at the specific filtering method:

Source code 3: doFilterInternal method of CsrfFilter

  1. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  2. request.setAttribute(HttpServletResponse.class.getName(), response);
  3. CsrfToken csrfToken = this.tokenRepository.loadToken(request);
  4. boolean missingToken = csrfToken == null;
  5. if(missingToken) {//If the token is empty, it means the first visit and a token object is generated
  6. csrfToken = this.tokenRepository.generateToken(request);
  7. this.tokenRepository.saveToken(csrfToken, request, response);
  8. }
  9. request.setAttribute(CsrfToken.class.getName(), csrfToken);
  10. //Put the token object into the request, note that the key here is csrfToken.getParameterName()= _csrf, so it is so hard to write on our page.
  11. request.setAttribute(csrfToken.getParameterName(), csrfToken);
  12. //This macher is the filter we customized in the Spring configuration file, that is, GET, HEAD, TRACE, OPTIONS and our rest are not processed
  13. if(!this.requireCsrfProtectionMatcher.matches(request)) {
  14. filterChain.doFilter(request, response);
  15. } else {
  16. String actualToken = request.getHeader(csrfToken.getHeaderName());
  17. if(actualToken == null) {
  18. actualToken = request.getParameter(csrfToken.getParameterName());
  19. }
  20. if(!csrfToken.getToken().equals(actualToken)) {
  21. if(this.logger.isDebugEnabled()) {
  22. this.logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request));
  23. }
  24. if(missingToken) {
  25. this.accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken));
  26. } else {
  27. this.accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken));
  28. }
  29. } else {
  30. filterChain.doFilter(request, response);
  31. }
  32. }
  33. }
copy code

As you can see from the source code, token verification is required for post requests other than our custom filters.

Originally, I wanted to take a screenshot to get a case, and then use the breakpoint to see the value transfer situation on the page and the background.... However, I can't upload pictures here to get crazy. Well, that's all to sum up! If you write something wrong or have other questions, please leave a message to exchange.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325234852&siteId=291194637