SpringSecurity anti Csrf attack

CSRF(Cross-site request forgery)Cross-site request forgery, also known as One Click Attackor Session Riding, commonly abbreviated CSRFor XSRFis a malicious use of the site. Although it sounds like a cross-site scripting ( XSS), but it XSSis very different from XSSthe use of trusted users within the site, and CSRFthe site by pretending to be trusted by the user's request to use trusted. And XSScompared to the attacks, the CSRFattacks are often not popular (and therefore their resources to guard against is quite rare) and difficult to prevent, than it is considered XSSmore dangerous. 
CSRFIs a web browser-dependent, confused over agent attacks ( deputy attack).

How to defend

When using the POST request, indeed avoid the problem automatically initiate a GET request such as img, script, iframe tags, etc., but this does not prevent the occurrence of CSRF attacks. Some malicious attack sites constructed in the form of a request form

public final class CsrfFilter extends OncePerRequestFilter {
    public static final RequestMatcher DEFAULT_CSRF_MATCHER = new
            CsrfFilter.DefaultRequiresCsrfMatcher();
    private final Log logger = LogFactory.getLog(this.getClass());
    private final CsrfTokenRepository tokenRepository;
    private RequestMatcher requireCsrfProtectionMatcher;
    private AccessDeniedHandler accessDeniedHandler;
    public CsrfFilter(CsrfTokenRepository csrfTokenRepository) {
        this= .requireCsrfProtectionMatcher DEFAULT_CSRF_MATCHER;
         the this .accessDeniedHandler = new new AccessDeniedHandlerImpl (); 
        Assert.notNull (csrfTokenRepository, "Not csrfTokenRepository CAN BE null" );
         the this .tokenRepository = csrfTokenRepository; 
    } 
    // by SpringSecurity can be seen here the way into the request mechanism csrf two to process 
    protected  void doFilterInternal (the HttpServletRequest Request, the HttpServletResponse Response, 
                                    the filterChain filterChain) throws ServletException, IOException { 
        request.setAttribute (the HttpServletResponse. class.getName(), response);
        CsrfToken csrfToken = this.tokenRepository.loadToken(request);
        boolean missingToken = csrfToken == null;
        if (missingToken) {
            csrfToken = this.tokenRepository.generateToken(request);
            this.tokenRepository.saveToken(csrfToken, request, response);
        }
        request.setAttribute(CsrfToken.class.getName(), csrfToken);
        request.setAttribute(csrfToken.getParameterName(), csrfToken);
//第一类:"GET", "HEAD", "TRACE", "OPTIONS"四类请求可以直接通过
        if (!the this .requireCsrfProtectionMatcher.matches (Request)) { 
            the FilterChain.doFilter (Request, Response); 
        } the else {
 // II: removing the above four categories, including the POST must be validated token can be carried by a 
            String actualToken = request.getHeader ( csrfToken.getHeaderName ());
             IF (actualToken == null ) { 
                actualToken = request.getParameter (csrfToken.getParameterName ()); 
            } 
            IF (! csrfToken.getToken () the equals (actualToken)) {.
                 IF ( the this .logger. IsDebugEnabled ()) {
                     the 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);
            }
        }
    }
    public void setRequireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher) {
        Assert.notNull(requireCsrfProtectionMatcher, "requireCsrfProtectionMatcher cannot be
        null");
        this.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;
    }
    public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
        Assert.notNull(accessDeniedHandler, "accessDeniedHandler cannot be null");
        this.accessDeniedHandler = accessDeniedHandler;
    }
    private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
        private final HashSet<String> allowedMethods;
        private DefaultRequiresCsrfMatcher() {
            this.allowedMethods = new HashSet(Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
    }
        public boolean matches(HttpServletRequest request) {
            return !this.allowedMethods.contains(request.getMethod());
        }
    }
}

Disable Csrf

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
//关闭打开的csrf保护
    .csrf().disable();
}
}

Csrf Token

When the user logs in, the system issues a CsrfToken value, the user carries the CsrfToken value and user name, password and other parameters to complete the login. CsrfToken system records the session value, any request after the user must bring the CsrfToken value by the system for verification.
This method needs to work with the front end, comprising a storage CsrfToken value, and carrying any request (including forms and Ajax) CsrfToken value. Security compared to HTTP Referer improve a lot if we are all XMLHttpRequest, you can add CsrfToken uniform value; but if there are a lot of forms and a label, it will become very cumbersome.

SpringSecurity used Csrf Token

Spring Security by registering a CsrfFilter to specifically handle CSRF attacks, in the Spring Security, CsrfToken Token is used to describe a value, and a parameter which interface the request header field or request verification should be obtained

public interface CsrfToken extends Serializable {
    String getHeaderName();
    String getParameterName();
    String getToken();
}
//CsrfTokenRepository则定义了如何生成、保存以及加载CsrfToken。
public interface CsrfTokenRepository {
    CsrfToken generateToken(HttpServletRequest request);
    void saveToken(CsrfToken token, HttpServletRequest request,
                   HttpServletResponse response);
    CsrfToken loadToken(HttpServletRequest request);
}

 HttpSessionCsrfTokenRepository

By default, Spring Security is a load HttpSessionCsrfTokenRepository
HttpSessionCsrfTokenRepository CsrfToken the value stored in the HttpSession, and the distal end CsrfToken specified values in the request parameter named "_csrf" or called "X-CSRF-TOKEN" request header field (you can call the appropriate set method to reset). When the check values are the same through comparison CsrfToken CsrfToken HttpSession value stored within the tip carrying, can determine whether this request is a CSRF attacks.

<input type='hidden' name='${_csrf.parameterName}' value='${_csrf.token}'>

 

In this way some of the limitations of single-page applications is relatively large, lack of flexibility.

CookieCsrfTokenRepository

Spring Security also provides another way that CookieCsrfTokenRepository
CookieCsrfTokenRepository is a more flexible and viable option, it will CsrfToken value is stored in the user's cookie. HttpSession storage server reduces memory consumption, and when the value of a cookie store CsrfToken, JavaScript can be used to read the front end (httpOnly property needs to be set to the cookie to false), without requiring a server implantation parameters, more flexibility in use.

CSRF is stored can not be utilized in a cookie, cookie can only be read in the case of the same domain, so eliminate the possibility of cross-domain to obtain third-party sites CsrfToken value. CSRF attacks themselves are not aware of the contents of the cookie, just use the vulnerability when a request is automatically carried cookie can be authenticated. But the server check for CsrfToken value is not taken from the cookie, but rather the front manual will CsrfToken value as a parameter in the request to carry

The following is a filtration process csrfFilter

@Override
     protected  void doFilterInternal (the HttpServletRequest Request, 
            the HttpServletResponse Response, the FilterChain filterChain) 
                    throws ServletException, IOException { 
        request.setAttribute (the HttpServletResponse. Class .getName (), Response); 
                
                // acquired in the cookie csrf Token (CookieTokenRepository) from the session or acquired (HttpSessionCsrfTokenRepository) 
        csrfToken csrfToken = the this .tokenRepository.loadToken (request);
         Final  boolean missingToken = csrfToken == null ;
                 // load less, then prove that the request was first initiated, should generate and save a new csrfToken value 
        if(missingToken) { 
            csrfToken = the this .tokenRepository.generateToken (Request);
             the this .tokenRepository.saveToken (csrfToken, Request, Response); 
        } 
        request.setAttribute (. CsrfToken class .getName (), csrfToken); 
        request.setAttribute (csrfToken. GetParameterName (), csrfToken); 

                // exclude some method does not require verification request CSRF attack (ignoring the default GET, HEAD, TRACE, and the OPTIONS) 
        IF (! the this ) {.requireCsrfProtectionMatcher.matches (request) 
            the FilterChain.doFilter (request, Response); 
            return ; 
        } 

                // actual token acquired from a parameter or in the header
        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));
            }
            return;
        }

        filterChain.doFilter(request, response);
    }

Users want to stick CSRF Token in a cookie. By default CookieCsrfTokenRepository of the XSRF-TOKEN cookie and read or write a file called HTTP parameter named _csrf from the head of the X-XSRF-TOKEN.

 
// code is as follows: 
 
.. .AND () CSRF () csrfTokenRepository (CookieCsrfTokenRepository.withHttpOnlyFalse ())

 

We are in daily use, can be used to add header or param way csrf_token, the following demonstration obtain token from the cookie

<form action="/executeLogin" method="post">
<p>Sign in to continue</p>
<div class="lowin-group">
    <label>用户名 <a href="#" class="login-back-link">Sign in?</a></label>
    <input type="text" name="username" class="lowin-input">
</div>
<div class="lowin-group password-group">
    <label>密码 <a href="#" class="forgot-link">Forgot Password?</a></label>
    <input type="password" name="password" class="lowin-input">
</div>
<div class="lowin-group">
    <label>验证码</label>
    <input type="text" name="kaptcha" class="lowin-input">
    <img src="/kaptcha.jpg" alt="kaptcha" height="50px" width="150px" style="margin-left: 20px">
</div>
<div class="lowin-group">
    <label>记住我</label>
    <input name="remember-me" type="checkbox" value="true" />
</div>
<input type="hidden" name="_csrf">
<input class="lowin-btn login-btn" type="submit">
</form>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>   
<script>
    $(function () {
        var aCookie = document.cookie.split("; ");
        console.log(aCookie);
        =inwas(for0; i < aCookie.length; i++)
        {
            var aCrumb = aCookie[i].split("=");
            if ("XSRF-TOKEN" == aCrumb[0])
                $("input[name='_csrf']").val(aCrumb[1]);
        }
    });
</script>

Precautions

springSecurity release default configuration, need not be detected by the filter csrfFilter http access mode

    private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
        private final HashSet<String> allowedMethods = new HashSet<>(
                Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
        @Override
        public boolean matches(HttpServletRequest request) {
            return !this.allowedMethods.contains(request.getMethod());
        }
    }

The reason why there above default GET, HEAD, TRACE, OPTIONS way, because

  1. If the http request is initiated by way of get requests, it just means that the resource access to the server, just the query, no update server resources, so for such requests, spring security defense strategy is permitted;

  2. If the http request is initiated by a request by post, then spring security is the default intercept such requests
    because such requests are dangerous operations with the updated server resources, if a malicious third party can update the server resources by hijacking session id, that cause the server data have been illegally tampered with, so such requests will be intercepted Spring security in case of default, spring security is enabled csrf blocking feature, which will result in cross-domain situation, post submission the request will be blocked and can not be processed (including reasonable post request), initiated by the front end of the back-end post request can not be processed normally, although to ensure the cross-domain security, but affect the normal use, if you turn off csrf protection, although normal processing can post requests, but can not prevent illegal post a request by hijacking the session id, so spring security to distinguish legitimate right post request, using the mechanisms of the token.

Guess you like

Origin www.cnblogs.com/dalianpai/p/12393133.html