SpringSecurity 中的 CSRF實現


1. 基礎知識##

csrf就是誘導已登錄過的用戶在不知情的情況下,使用自己的登錄憑據來完成一些不可告人之事。比如利用img標簽或者script標簽的src屬性自動訪問一些敏感api,或者是偽造一個form標簽,action寫的是一些敏感api,通過js自動提交表單等。

1.1 防御手段###

原則上修改功能的API,都要避免使用GET方式。然后就是兩種防護手段,一個是校驗referer,一個是csrftoken,前者用curl就能破,后者稍微麻煩一點點,也能破。雖然沒法完美防御,但是網站這些基礎功能還是要有,要不然漏掃都過不去。

2. springboot中的實現##

springboot是用的csrftoken值來實現的,就是每個post請求會生成一個token,這個值不在cookie里面,所以偽造沒用,到時服務器端會進行比對,發現不一致就拒絕服務。
弊端就是改造舊系統時要每個form都要改,ajax那種post的提交也需要寫額外的函數獲取token在放到所有請求里面,所以這個功能要提前規划,后面再改就比較麻煩了。

和cors類似,也是用了一個filter,CsrfFilter來實現的過濾功能,底層結構是HttpSessionCsrfTokenRepository,提供了3個方法。

CsrfFilter.java

    @Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
					throws ServletException, IOException {
		request.setAttribute(HttpServletResponse.class.getName(), response);
                //獲取服務器保存的token
		CsrfToken csrfToken = this.tokenRepository.loadToken(request);
		final boolean missingToken = csrfToken == null;
                //缺少token 重新生成並保存
		if (missingToken) {
			csrfToken = this.tokenRepository.generateToken(request);
			this.tokenRepository.saveToken(csrfToken, request, response);
		}
		request.setAttribute(CsrfToken.class.getName(), csrfToken);
		request.setAttribute(csrfToken.getParameterName(), csrfToken);
                //如果url不匹配需要校驗的csrf 就直接略過
		if (!this.requireCsrfProtectionMatcher.matches(request)) {
			filterChain.doFilter(request, response);
			return;
		}
                //獲得客戶端token
		String actualToken = request.getHeader(csrfToken.getHeaderName());
		if (actualToken == null) {
			actualToken = request.getParameter(csrfToken.getParameterName());
		}
                //token不匹配
		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);
	}

然后就是tokenRepository,基本都是使用LazyCsrfTokenRepository封裝了HttpSessionCsrfTokenRepository。作用就是只有在實際取token時才會保存session,節省服務器資源,HttpSessionCsrfTokenRepository實現了CsrfTokenRepository接口定義三個關於token的方法

    CsrfToken generateToken(HttpServletRequest request);

    void saveToken(CsrfToken token, HttpServletRequest request,
			HttpServletResponse response);

    CsrfToken loadToken(HttpServletRequest request);

3. 如何開啟csrf防御##

csrf默認是開啟的,配下忽略的url就可以了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM