欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringSecurity的防Csrf攻擊實(shí)現(xiàn)代碼解析

 更新時(shí)間:2020年03月03日 14:25:58   作者:天宇軒-王  
這篇文章主要介紹了SpringSecurity的防Csrf攻擊實(shí)現(xiàn)代碼解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

CSRF(Cross-site request forgery)跨站請(qǐng)求偽造,也被稱為One Click Attack或者Session Riding,通??s寫為CSRF或XSRF,是一種對(duì)網(wǎng)站的惡意利用。盡管聽起來像跨站腳本(XSS),但它與XSS非常不同,XSS利用站點(diǎn)內(nèi)的信任用戶,而CSRF則通過偽裝成受信任用戶的請(qǐng)求來利用受信任的網(wǎng)站。與XSS攻擊相比,CSRF攻擊往往不大流行(因此對(duì)其進(jìn)行防范的資源也相當(dāng)稀少)和難以防范,所以被認(rèn)為比XSS更具危險(xiǎn)性。

CSRF是一種依賴web瀏覽器的、被混淆過的代理人攻擊(deputy attack)。

如何防御

使用POST請(qǐng)求時(shí),確實(shí)避免了如img、script、iframe等標(biāo)簽自動(dòng)發(fā)起GET請(qǐng)求的問題,但這并不能杜絕CSRF攻擊的發(fā)生。一些惡意網(wǎng)站會(huì)通過表單的形式構(gòu)造攻擊請(qǐng)求

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;
  this.accessDeniedHandler = new AccessDeniedHandlerImpl();
  Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
  this.tokenRepository = csrfTokenRepository;
 }
 //通過這里可以看出SpringSecurity的csrf機(jī)制把請(qǐng)求方式分成兩類來處理
 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) {
   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"四類請(qǐng)求可以直接通過
  if (!this.requireCsrfProtectionMatcher.matches(request)) {
   filterChain.doFilter(request, response);
  } else {
//第二類:除去上面四類,包括POST都要被驗(yàn)證攜帶token才能通過
   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);
   }
  }
 }
 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());
  }
 }
}

禁用Csrf

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
 http
//關(guān)閉打開的csrf保護(hù)
 .csrf().disable();
}
}

用戶登錄時(shí),系統(tǒng)發(fā)放一個(gè)CsrfToken值,用戶攜帶該CsrfToken值與用戶名、密碼等參數(shù)完成登錄。系統(tǒng)記錄該會(huì)話的 CsrfToken 值,之后在用戶的任何請(qǐng)求中,都必須帶上該CsrfToken值,并由系統(tǒng)進(jìn)行校驗(yàn)。

這種方法需要與前端配合,包括存儲(chǔ)CsrfToken值,以及在任何請(qǐng)求中(包括表單和Ajax)攜帶CsrfToken值。安全性相較于HTTP Referer提高很多,如果都是XMLHttpRequest,則可以統(tǒng)一添加CsrfToken值;但如果存在大量的表單和a標(biāo)簽,就會(huì)變得非常煩瑣。

SpringSecurity中使用Csrf Token

Spring Security通過注冊(cè)一個(gè)CsrfFilter來專門處理CSRF攻擊,在Spring Security中,CsrfToken是一個(gè)用于描述Token值,以及驗(yàn)證時(shí)應(yīng)當(dāng)獲取哪個(gè)請(qǐng)求參數(shù)或請(qǐng)求頭字段的接口

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

在默認(rèn)情況下,Spring Security加載的是一個(gè)HttpSessionCsrfTokenRepository

HttpSessionCsrfTokenRepository 將 CsrfToken 值存儲(chǔ)在 HttpSession 中,并指定前端把CsrfToken 值放在名為“_csrf”的請(qǐng)求參數(shù)或名為“X-CSRF-TOKEN”的請(qǐng)求頭字段里(可以調(diào)用相應(yīng)的設(shè)置方法來重新設(shè)定)。校驗(yàn)時(shí),通過對(duì)比HttpSession內(nèi)存儲(chǔ)的CsrfToken值與前端攜帶的CsrfToken值是否一致,便能斷定本次請(qǐng)求是否為CSRF攻擊。

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

這種方式在某些單頁(yè)應(yīng)用中局限性比較大,靈活性不足。

CookieCsrfTokenRepository

Spring Security還提供了另一種方式,即CookieCsrfTokenRepository

CookieCsrfTokenRepository 是一種更加靈活可行的方案,它將 CsrfToken 值存儲(chǔ)在用戶的cookie內(nèi)。減少了服務(wù)器HttpSession存儲(chǔ)的內(nèi)存消耗,并且當(dāng)用cookie存儲(chǔ)CsrfToken值時(shí),前端可以用JavaScript讀取(需要設(shè)置該cookie的httpOnly屬性為false),而不需要服務(wù)器注入?yún)?shù),在使用方式上更加靈活。

存儲(chǔ)在cookie中是不可以被CSRF利用的,cookie 只有在同域的情況下才能被讀取,所以杜絕了第三方站點(diǎn)跨域獲取 CsrfToken 值的可能。CSRF攻擊本身是不知道cookie內(nèi)容的,只是利用了當(dāng)請(qǐng)求自動(dòng)攜帶cookie時(shí)可以通過身份驗(yàn)證的漏洞。但服務(wù)器對(duì) CsrfToken 值的校驗(yàn)并非取自 cookie,而是需要前端手動(dòng)將CsrfToken值作為參數(shù)攜帶在請(qǐng)求里

下面是csrfFilter的過濾過程

@Override
 protected void doFilterInternal(HttpServletRequest request,
   HttpServletResponse response, FilterChain filterChain)
     throws ServletException, IOException {
  request.setAttribute(HttpServletResponse.class.getName(), response);
    
    //獲取到cookie中的csrf Token(CookieTokenRepository)或者從session中獲取(HttpSessionCsrfTokenRepository)
  CsrfToken csrfToken = this.tokenRepository.loadToken(request);
  final boolean missingToken = csrfToken == null;
    //加載不到,則證明請(qǐng)求是首次發(fā)起的,應(yīng)該生成并保存一個(gè)新的 CsrfToken 值
  if (missingToken) {
   csrfToken = this.tokenRepository.generateToken(request);
   this.tokenRepository.saveToken(csrfToken, request, response);
  }
  request.setAttribute(CsrfToken.class.getName(), csrfToken);
  request.setAttribute(csrfToken.getParameterName(), csrfToken);

    //排除部分不需要驗(yàn)證CSRF攻擊的請(qǐng)求方法(默認(rèn)忽略了GET、HEAD、TRACE和OPTIONS)
  if (!this.requireCsrfProtectionMatcher.matches(request)) {
   filterChain.doFilter(request, response);
   return;
  }

    //實(shí)際的token從header或者parameter中獲取
  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);
 }

用戶想要堅(jiān)持CSRF Token在cookie中。 默認(rèn)情況下CookieCsrfTokenRepository將編寫一個(gè)名為 XSRF-TOKEN的cookie和從頭部命名 X-XSRF-TOKEN中讀取或HTTP參數(shù) _csrf。

//代碼如下:
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())

我們?cè)谌粘J褂弥校梢圆捎胔eader或者param的方式添加csrf_token,下面示范從cookie中獲取token

<form action="/executeLogin" method="post">
<p>Sign in to continue</p>
<div class="lowin-group">
 <label>用戶名 <a href="#" rel="external nofollow" rel="external nofollow" 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="#" rel="external nofollow" rel="external nofollow" class="forgot-link">Forgot Password?</a></label>
 <input type="password" name="password" class="lowin-input">
</div>
<div class="lowin-group">
 <label>驗(yàn)證碼</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);
  for (var i=0; i < aCookie.length; i++)
  {
   var aCrumb = aCookie[i].split("=");
   if ("XSRF-TOKEN" == aCrumb[0])
    $("input[name='_csrf']").val(aCrumb[1]);
  }
 });
</script>

注意事項(xiàng)

springSecurity配置了默認(rèn)放行, 不需要通過csrfFilter過濾器檢測(cè)的http訪問方式

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());
  }
 }

之所以會(huì)有上面默認(rèn)的GET,HEAD,TRACE,OPTIONS方式,是因?yàn)?/p>

如果這個(gè)http請(qǐng)求是通過get方式發(fā)起的請(qǐng)求,意味著它只是訪問服務(wù)器 的資源,僅僅只是查詢,沒有更新服務(wù)器的資源,所以對(duì)于這類請(qǐng)求,spring security的防御策略是允許的;

如果這個(gè)http請(qǐng)求是通過post請(qǐng)求發(fā)起的, 那么spring security是默認(rèn)攔截這類請(qǐng)求的

因?yàn)檫@類請(qǐng)求是帶有更新服務(wù)器資源的危險(xiǎn)操作,如果惡意第三方可以通過劫持session id來更新 服務(wù)器資源,那會(huì)造成服務(wù)器數(shù)據(jù)被非法的篡改,所以這類請(qǐng)求是會(huì)被Spring security攔截的,在默認(rèn)的情況下,spring security是啟用csrf 攔截功能的,這會(huì)造成,在跨域的情況下,post方式提交的請(qǐng)求都會(huì)被攔截?zé)o法被處理(包括合理的post請(qǐng)求),前端發(fā)起的post請(qǐng)求后端無法正常 處理,雖然保證了跨域的安全性,但影響了正常的使用,如果關(guān)閉csrf防護(hù)功能,雖然可以正常處理post請(qǐng)求,但是無法防范通過劫持session id的非法的post請(qǐng)求,所以spring security為了正確的區(qū)別合法的post請(qǐng)求,采用了token的機(jī)制 。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • JAVA日志框架之JUL、JDK原生日志框架詳解

    JAVA日志框架之JUL、JDK原生日志框架詳解

    Java語(yǔ)言的強(qiáng)大之處就是因?yàn)樗鼜?qiáng)大而且成熟的生態(tài)體系,其中包括日志框架,下面這篇文章主要給大家介紹了關(guān)于JAVA日志框架之JUL、JDK原生日志框架的相關(guān)資料,需要的朋友可以參考下
    2024-01-01
  • SpringBoot如何訪問jsp頁(yè)面

    SpringBoot如何訪問jsp頁(yè)面

    本文介紹了如何在Spring Boot項(xiàng)目中進(jìn)行Web開發(fā),包括創(chuàng)建項(xiàng)目、配置文件、添加依賴、控制層修改、測(cè)試效果以及在IDEA中進(jìn)行配置的詳細(xì)步驟
    2025-01-01
  • Java 生成隨機(jī)字符串?dāng)?shù)組的實(shí)例詳解

    Java 生成隨機(jī)字符串?dāng)?shù)組的實(shí)例詳解

    這篇文章主要介紹了Java 生成隨機(jī)字符串?dāng)?shù)組的實(shí)例詳解的相關(guān)資料,主要是利用Collections.sort()方法對(duì)泛型為String的List 進(jìn)行排序,需要的朋友可以參考下
    2017-08-08
  • SpringCloud Nacos配置中心管理超詳細(xì)講解

    SpringCloud Nacos配置中心管理超詳細(xì)講解

    這篇文章主要介紹了Springcloud中的Nacos服務(wù)配置,本文以用戶微服務(wù)為例,進(jìn)行統(tǒng)一的配置,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-11-11
  • Java操作pdf的工具類itext的處理方法

    Java操作pdf的工具類itext的處理方法

    這篇文章主要介紹了Java操作pdf的工具類itext,iText是一種生成PDF報(bào)表的Java組件,通過在服務(wù)器端使用Jsp或JavaBean生成PDF報(bào)表,客戶端采用超鏈接顯示或下載得到生成的報(bào)表,需要的朋友可以參考下
    2022-04-04
  • Java利用反射動(dòng)態(tài)設(shè)置對(duì)象字段值的實(shí)現(xiàn)

    Java利用反射動(dòng)態(tài)設(shè)置對(duì)象字段值的實(shí)現(xiàn)

    橋梁信息維護(hù)需要做到字段級(jí)別的權(quán)限控制,本文主要介紹了Java利用反射動(dòng)態(tài)設(shè)置對(duì)象字段值的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • Java中的AQS同步隊(duì)列問題詳解

    Java中的AQS同步隊(duì)列問題詳解

    AQS?提供一套基礎(chǔ)的機(jī)制來實(shí)現(xiàn)線程的同步、阻塞與喚醒、等待隊(duì)列等功能,也就是想要深入學(xué)習(xí)線程工具類,這個(gè)同步隊(duì)列就必須得掌握,這篇文章主要介紹了Java中的AQS同步隊(duì)列問題,需要的朋友可以參考下
    2022-06-06
  • Java(Springboot)項(xiàng)目調(diào)用第三方WebService接口實(shí)現(xiàn)代碼

    Java(Springboot)項(xiàng)目調(diào)用第三方WebService接口實(shí)現(xiàn)代碼

    這篇文章主要介紹了如何使用Java調(diào)用WebService接口,傳遞XML參數(shù),獲取XML響應(yīng),并將其解析為JSON格式,文中詳細(xì)描述了WSDL文檔的使用、HttpClientBuilder和Apache?Axis兩種調(diào)用方式的具體實(shí)現(xiàn)步驟,需要的朋友可以參考下
    2025-02-02
  • SpringCloud?Gateway之請(qǐng)求應(yīng)答日志打印方式

    SpringCloud?Gateway之請(qǐng)求應(yīng)答日志打印方式

    這篇文章主要介紹了SpringCloud?Gateway之請(qǐng)求應(yīng)答日志打印方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 詳談jvm--Java中init和clinit的區(qū)別

    詳談jvm--Java中init和clinit的區(qū)別

    下面小編就為大家?guī)硪黄斦刯vm--Java中init和clinit的區(qū)別。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10

最新評(píng)論