1. 程式人生 > 程式設計 >Spring Boot 中該如何防禦計時攻擊

Spring Boot 中該如何防禦計時攻擊

鬆哥最近在研究 Spring Security 原始碼,發現了很多好玩的程式碼,抽空寫幾篇文章和小夥伴們分享一下。

很多人吐槽 Spring Security 比 Shiro 重量級,這個重量級不是憑空來的,重量有重量的好處,就是它提供了更為強大的防護功能。

比如鬆哥最近看到的一段程式碼:

protected final UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication)
 throws AuthenticationException {
 prepareTimingAttackProtection();
 try {
 UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
 if (loadedUser == null) {
  throw new InternalAuthenticationServiceException(
   "UserDetailsService returned null,which is an interface contract violation");
 }
 return loadedUser;
 }
 catch (UsernameNotFoundException ex) {
 mitigateAgainstTimingAttack(authentication);
 throw ex;
 }
 catch (InternalAuthenticationServiceException ex) {
 throw ex;
 }
 catch (Exception ex) {
 throw new InternalAuthenticationServiceException(ex.getMessage(),ex);
 }
}

這段程式碼位於 DaoAuthenticationProvider 類中,為了方便大家理解,我來簡單說下這段程式碼的上下文環境。

當用戶提交使用者名稱密碼登入之後,Spring Security 需要根據使用者提交的使用者名稱去資料庫中查詢使用者,這塊如果大家不熟悉,可以參考鬆哥之前的文章:

  1. Spring Security 如何將使用者資料存入資料庫?
  2. Spring Security+Spring Data Jpa 強強聯手,安全管理只有更簡單!

查到使用者物件之後,再去比對從資料庫中查到的使用者密碼和使用者提交的密碼之間的差異。具體的比對工作,可以參考 Spring Boot 中密碼加密的兩種姿勢! 一文。

而上面這段程式碼就是 Spring Security 根據使用者登入時傳入的使用者名稱去資料庫中查詢使用者,並將查到的使用者返回。方法中還有一個 authentication 引數,這個引數裡邊儲存了使用者登入時傳入的使用者名稱/密碼資訊。

那麼這段程式碼有什麼神奇之處呢?

我們來一行一行分析。

原始碼梳理

1

首先方法一進來呼叫了 prepareTimingAttackProtection 方法,從方法名字上可以看出,這個是為計時攻擊的防禦做準備,那麼什麼又是計時攻擊呢?別急,鬆哥一會來解釋。我們先來吧流程走完。prepareTimingAttackProtection 方法的執行很簡單,如下:

private void prepareTimingAttackProtection() {
 if (this.userNotFoundEncodedPassword == null) {
 this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
 }
}

該方法就是將常量 USER_NOT_FOUND_PASSWORD 使用 passwordEncoder 編碼之後(如果不瞭解 passwordEncoder,可以參考 Spring Boot 中密碼加密的兩種姿勢! 一文),將編碼結果賦值給 userNotFoundEncodedPassword 變數。

2

接下來呼叫 loadUserByUsername 方法,根據登入使用者傳入的使用者名稱去資料庫中查詢使用者,如果查到了,就將查到的物件返回。

3

如果查詢過程中丟擲 UsernameNotFoundException 異常,按理說直接丟擲異常,接下來的密碼比對也不用做了,因為根據使用者名稱都沒查到使用者,這次登入肯定是失敗的,沒有必要進行密碼比對操作!

但是大家注意,在丟擲異常之前呼叫了 mitigateAgainstTimingAttack 方法。這個方法從名字上來看,有緩解計時攻擊的意思。

我們來看下該方法的執行流程:

private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {
 if (authentication.getCredentials() != null) {
 String presentedPassword = authentication.getCredentials().toString();
 this.passwordEncoder.matches(presentedPassword,this.userNotFoundEncodedPassword);
 }
}

可以看到,這裡首先獲取到登入使用者傳入的密碼即 presentedPassword,然後呼叫 passwordEncoder.matches 方法進行密碼比對操作,本來該方法的第二個引數是資料庫查詢出來的使用者密碼,現在資料庫中沒有查到使用者,所以第二個引數用 userNotFoundEncodedPassword 代替了,userNotFoundEncodedPassword 就是我們一開始呼叫 prepareTimingAttackProtection 方法時賦值的變數。這個密碼比對,從一開始就註定了肯定會失敗,那為什麼還要比對呢?

計時攻擊

這就引入了我們今天的主題--計時攻擊。

計時攻擊是旁路攻擊的一種,在密碼學中,旁道攻擊又稱側通道攻擊、邊通道攻擊(Side-channel attack)。

這種攻擊方式並非利用加密演算法的理論弱點,也不是暴力破解,而是從密碼系統的物理實現中獲取的資訊。例如:時間資訊、功率消耗、電磁洩露等額外的資訊源,這些資訊可被用於對系統的進一步破解。

旁路攻擊有多種不同的分類:

  • 快取攻擊(Cache Side-Channel Attacks),通過獲取對快取的訪問權而獲取快取內的一些敏感資訊,例如攻擊者獲取雲端主機物理主機的訪問權而獲取儲存器的訪問權。
  • 計時攻擊(Timing attack),通過裝置運算的用時來推斷出所使用的運算操作,或者通過對比運算的時間推定資料位於哪個儲存裝置,或者利用通訊的時間差進行資料竊取。
  • 基於功耗監控的旁路攻擊,同一裝置不同的硬體電路單元的運作功耗也是不一樣的,因此一個程式執行時的功耗會隨著程式使用哪一種硬體電路單元而變動,據此推斷出資料輸出位於哪一個硬體單元,進而竊取資料。
  • 電磁攻擊(Electromagnetic attack),裝置運算時會洩漏電磁輻射,經過得當分析的話可解析出這些洩漏的電磁輻射中包含的資訊(比如文字、聲音、影象等),這種攻擊方式除了用於密碼學攻擊以外也被用於非密碼學攻擊等竊聽行為,如TEMPEST 攻擊。
  • 聲學密碼分析(Acoustic cryptanalysis),通過捕捉裝置在運算時洩漏的聲學訊號捉取資訊(與功率分析類似)。
  • 差別錯誤分析,隱密資料在程式執行發生錯誤並輸出錯誤資訊時被發現。
  • 資料殘留(Data remanence),可使理應被刪除的敏感資料被讀取出來(例如冷啟動攻擊)。
  • 軟體初始化錯誤攻擊,現時較為少見,行錘攻擊(Row hammer)是該類攻擊方式的一個例項,在這種攻擊實現中,被禁止訪問的儲存器位置旁邊的儲存器空間如果被頻繁訪問將會有狀態保留丟失的風險。
  • 光學方式,即隱密資料被一些視覺光學儀器(如高清晰度相機、高清晰度攝影機等裝置)捕捉。

所有的攻擊型別都利用了加密/解密系統在進行加密/解密操作時演算法邏輯沒有被發現缺陷,但是通過物理效應提供了有用的額外資訊(這也是稱為“旁路”的緣由),而這些物理資訊往往包含了金鑰、密碼、密文等隱密資料。

而上面 Spring Security 中的那段程式碼就是為了防止計時攻擊。

具體是怎麼做的呢?假設 Spring Security 從資料庫中沒有查到使用者資訊就直接丟擲異常了,沒有去執行 mitigateAgainstTimingAttack 方法,那麼黑客經過大量的測試,再經過統計分析,就會發現有一些登入驗證耗時明顯少於其他登入,進而推斷出登入驗證時間較短的都是不存在的使用者,而登入耗時較長的是資料庫中存在的使用者。

現在 Spring Security 中,通過執行 mitigateAgainstTimingAttack 方法,無論使用者存在或者不存在,登入校驗的耗時不會有明顯差別,這樣就避免了計時攻擊。

可能有小夥伴會說,passwordEncoder.matches 方法執行能耗費多少時間呀?這要看你怎麼計時了,時間單位越小,差異就越明顯:毫秒(ms)、微秒(µs)、奈秒(ns)、皮秒(ps)、飛秒(fs)、阿秒(as)、仄秒(zs)。

另外,Spring Security 為了安全,passwordEncoder 中引入了一個概念叫做自適應單向函式,這種函式故意執行的很慢並且消耗大量系統資源,所以非常有必要進行計時攻擊防禦。

關於自適應單向函式,這是另外一個故事了,鬆哥抽空再和小夥伴們聊~

好啦,今天就先和小夥伴們聊這麼多,小夥伴們決定有收穫的話,記得點個在看鼓勵下鬆哥哦~

以上就是Spring Boot 中該如何防禦計時攻擊的詳細內容,更多關於Spring Boot 防禦計時攻擊的資料請關注我們其它相關文章!