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
- <properties>
- <spring.security.version>4.2.2.RELEASE</spring.security.version>
- </properties>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-core</artifactId>
- <version>${spring.security.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-web</artifactId>
- <version>${spring.security.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-config</artifactId>
- <version>${spring.security.version}</version>
- </dependency>
2.web.xml configuration
- <filter>
- <filter-name>springSecurityFilterChain</filter-name>
- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>springSecurityFilterChain</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
3. Spring configuration file configuration Zhejiang surrogacy company
- <bean id="csrfSecurityRequestMatcher" class="com.xxx.CsrfSecurityRequestMatcher"></bean>
- <security:http auto-config="true" use-expressions="true">
- <security:headers>
- <security:frame-options disabled="true"/>
- </security:headers>
- <security:csrf request-matcher-ref="csrfSecurityRequestMatcher" />
- </security:http>
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
- private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
- private final HashSet<String> allowedMethods;
- private DefaultRequiresCsrfMatcher() {
- this.allowedMethods = new HashSet(Arrays.asList(new String[]{"GET", "HEAD", "TRACE", "OPTIONS"}));
- }
- public boolean matches(HttpServletRequest request) {
- return !this.allowedMethods.contains(request.getMethod());
- }
- }
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
- public class CsrfSecurityRequestMatcher implements RequestMatcher {
- private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
- private RegexRequestMatcher unprotectedMatcher = new RegexRequestMatcher("^/rest/.*", null);
- @Override
- public boolean matches(HttpServletRequest request) {
- if(allowedMethods.matcher(request.getMethod()).matches()){
- return false;
- }
- return !unprotectedMatcher.matches(request);
- }
- }
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:
- <meta name="_csrf" content="${_csrf.token}"/>
- <meta name="_csrf_header" content="${_csrf.headerName}"/>
- <script>
- var token = $("meta[name='_csrf']").attr("content");
- var header = $("meta[name='_csrf_header']").attr("content");
- $.ajaxSetup({
- beforeSend: function (xhr) {
- if(header && token ){
- xhr.setRequestHeader(header, token);
- }
- }}
- );
- </script>
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
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
- request.setAttribute(HttpServletResponse.class.getName(), response);
- CsrfToken csrfToken = this.tokenRepository.loadToken(request);
- boolean missingToken = csrfToken == null;
- if(missingToken) {//If the token is empty, it means the first visit and a token object is generated
- csrfToken = this.tokenRepository.generateToken(request);
- this.tokenRepository.saveToken(csrfToken, request, response);
- }
- request.setAttribute(CsrfToken.class.getName(), csrfToken);
- //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.
- request.setAttribute(csrfToken.getParameterName(), csrfToken);
- //This macher is the filter we customized in the Spring configuration file, that is, GET, HEAD, TRACE, OPTIONS and our rest are not processed
- if(!this.requireCsrfProtectionMatcher.matches(request)) {
- filterChain.doFilter(request, response);
- } else {
- String actualToken = request.getHeader(csrfToken.getHeaderName());
- if(actualToken == null) {
- actualToken = request.getParameter(csrfToken.getParameterName());
- }
- if(!csrfToken.getToken().equals(actualToken)) {
- if(this.logger.isDebugEnabled()) {
- this.logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request));
- }
- if(missingToken) {
- this.accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken));
- } else {
- this.accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken));
- }
- } else {
- filterChain.doFilter(request, response);
- }
- }
- }
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.