Jenkins如何使用CrumbIssuer防禦CSRF攻擊

人艰不拆_zmc發表於2024-07-28

1、CSRF(跨站請求偽造)概述

  在講解Jenkins的跨站請求偽造(CSRF)保護機制之前,讓我們首先對CSRF這一安全威脅及其重要性進行簡明扼要的概述。

1.1 CSRF(跨站請求偽造)的原理

  CSRF(即跨站請求偽造)是指利用受害者尚未失效的身份認證資訊、(cookie、會話等),誘騙其點選惡意連結或者訪問包含攻擊程式碼的頁面,在受害人不知情的情況下以受害人的身份向(身份認證資訊所對應的)伺服器傳送請求,從而完成非法操作(轉賬,改密碼等)。CSRF屬於業務邏輯漏洞,在伺服器看來,所有請求都是合法正常的(請求是經過身份認證的)。

  因為CSRF攻擊,會重複利用使用者的 Cookie,而說到 Cookie,就得先從 HTTP 協議開始講起。

1.1.1 HTTP協議

無記憶性:

HTTP是一種無狀態協議,即伺服器不會保留與客戶交易時的狀態。

使用者A在很短的時間間隔內向Web伺服器傳送了兩次同樣的請求,伺服器並不會因為已經響應了該請求一次就不對第二次請求進行響應,因為伺服器並不知道已經響應過一次該請求。

假設使用者在網站A的某一個頁面上已經完成了登入操作,當在該網站的另一個頁面上執行的操作需要驗證使用者登入的時候仍然需要使用者再次登入,因為HTTP並不知道你已經登入了,它不會維持你的登入狀態。

因為要多次登入太過麻煩,為了讓伺服器能夠記住使用者引入了 Cookie 機制。

1.1.2 Cookie機制

當使用者訪問站點的時候,站點會為該使用者分配一個 Cookie 值,站點使用該 Cookie 值來標記使用者,當使用者瀏覽器接受到包含 Cookie 值得資料包後,會將 Cookie 得值取出,存放到瀏覽器中,隨後瀏覽器會在發往該站點得資料包中自動得填充該 Cookie 值。Cookie 的值的填充是瀏覽器的行為。

1.1.3 CSRF(跨站請求偽造)的原理

當瀏覽器自動完成Cookie的填充,目標網站會誤認為該資料包就是管理員傳送的,會以管理員的許可權進行相關的操作。

1.2 CSRF(跨站請求偽造)常見防禦方案

之所以會出現 CSRF 的攻擊,本質原因是駭客可以偽造使用者的請求,使用者的請求資訊實際上是存在 Cookie 中的,所以駭客可以在不知道上述那些驗證資訊直接跳過安全驗證。所以防禦 CSRF 的關鍵點在於當使用者的請求在發出的時候,駭客不能去偽造這個資訊,並且這個資訊不能存在於 cookie 之中。

1.2.1 新增HTTP Referer

Referer 是存在於 HTTP 報文頭部的一個欄位,它是由瀏覽器提供的,可以記錄當前請求的來源地址(標明請求的來源)。駭客在偽造請求時只能在自己的網站上構造請求,這樣的話伺服器驗證 Referer 就可以知道這個請求並不是自己網站內部的請求,而是駭客偽造的,直接拒絕。以百度裡面的一個檔案為例:

使用這種方式的好處是簡單、方便,一般的開發人員就不用去操心 CSRF 的安全性漏洞了,只需要在服務端加一個攔截器,去驗證請求的 Referer 值就可以了。這種驗證 Referer 的方式固然簡單高效,但是也並不是萬無一失的,雖然說 Referer 的值是由瀏覽器提供的,但是在某些瀏覽器上駭客是可以在傳送請求時去篡改 Referer 或其他 Header 的值的,這樣的話就相當於跳過了驗證,從而又可以進行 CSRF 攻擊了。

1.2.2 Anti CSRF Token

Anti CSRF Token。Token,就是令牌,最大的特點就是隨機性,不可預測。

Anti CSRF Token 原理上是透過 Session Token 來實現的。當客戶端請求頁面時,伺服器會生成一個隨機數 Token,並且將 Token 放置到 Session 當中,然後將 Token 發給客戶端(一般透過構造 Hidden 表單)。下次客戶端提交請求時,Token 會隨著表單一起提交到伺服器端,伺服器端會在收到請求後用攔截器去對 Token 的值進行驗證,判斷是否和 Session 中的 Token 值相等,若相等,則可以證明請求有效,不是偽造的,如果沒有Token或者Token不正確都會被認為是攻擊而直接丟棄。

GET請求

http://url?csrftoken=tokenvalue

POST請求

<input type="hidden" name="csrftoken" value="tokenvalue" />

1.2.3 使用者驗證

對於敏感操作,可以要求使用者進行額外的身份驗證(例如輸入密碼或驗證碼)。

1.2.4 避免使用 GET 請求進行狀態改變

確保狀態改變操作(例如資料修改、刪除)使用 POST、PUT 或 DELETE 方法,而不是 GET 方法。GET 請求應只用於資料獲取操作。

2、Jenkins跨站請求偽造保護功能

Jenkins的CSRF保護使用Token(在Jenkins裡叫Crumb),它由Jenkins建立,併傳送給使用者。任何導致修改的表單提交或者類似的操作,比如:觸發任務或修改構建配置,都需要提供Crumb。Crumb包含標識建立它的使用者的資訊,因此使用其他使用者令牌的提交將被拒絕。所有這些都發生在後臺,除了極少數情況外,沒有明顯的影響,例如,在使用者的會話到期後,他們再次登入。

2.1 跨站請求偽造保護功能配置

Dashboard » Manage Jenkins » Security 中,管理員可以配置 CSRF Protection。另外,從Jenkins 2.214和Jenkins LTS 2.222.1開始,Jenkins預設啟用跨站請求偽造保護功能,並且在Jenkins圖形化頁面不允許關閉。

預設啟用的The *Default Crumb Issuer, 這個攔截器會計算客戶端傳遞的Crumb值的hash是否是可用hash, 這個hash的來源是透過使用者名稱、sessionID、請求IP以及訪問的jenkins的唯一標識生成的。

唯一支援的選項“啟用代理相容”會從令牌中刪除有關使用者IP地址的資訊。當Jenkins在反向代理後面執行並且從Jenkins看到的使用者IP地址會定期更改時,這可能很有用。

注意 1:預設的Crumb Issuer生成Crumb的雜湊,並在其中編碼以下的資訊(如果啟用代理相容,生成的Crumb雜湊時不再使用請求Ip):
  • 為其生成Crumb的使用者名稱
  • 生成Crumb的網路會話 ID
  • 為其生成Crumb的使用者的 IP 地址
  • 此 Jenkins 例項獨有的salt

當Crumb被髮送回 Jenkins 時,所有這些資訊都需要匹配,以便該提交被視為有效。

注意 2:CrumbIssuer是一個為了抵禦CSRF攻擊而生成名為Crumb值的演算法。Crumb通常是對能夠唯一標識傳送請求的代理(使用者端)的資訊的hash值,並且其會被加密以防第三方偽造。Crumb值由伺服器端在使用者登入時生成併傳送給使用者,之後使用者每次登入時都需要對其進行驗證。為了保證Crumb不會被猜測或者偽造,生成方式是獲取使用者資訊進行hash並加密。

2.2 在API裡使用Crumb

發給Jenkins的POST請求通常需要提供Crumb。這也適用於使用使用者名稱和密碼進行身份驗證的指令碼客戶端。由於 Crumb 包含網路會話 ID,客戶端需要執行以下操作:

  • 向 /CrumbIssuer/api 傳送請求,請求Crumb,注意 Set-Cookie響應頭
  • 對於所有後續請求,除了使用者名稱和密碼之外,還提供 Crumb 和會話 cookie。

示例(禁用流水線專案test-crumb):

透過API禁用流水線專案前先檢視流水線狀態。

透過API禁用流水線:

curl -X POST -u zmc:123456 "http://10.20.31.153:8080/jenkins/job/test-crumb/disable"  

報403錯誤(鑑權失敗),並且日誌給出了明確的報錯原因,請求裡面缺少合法的Crumb。在第二章節介紹Jenkins跨站請求偽造保護功能時已經講過任何導致修改的表單提交或者類似的操作,都需要提供Crumb。

那麼我們就為介面提供Crumb值,透過API獲取當前使用者的Crumb。

curl -s -u zmc:123456 http://10.20.31.153:8080/jenkins/crumbIssuer/api/json 

接下來請求頭裡面帶著當前使用者的Crumb值,再次禁用流水線。

curl -X POST -u zmc:123456 -H "Jenkins-Crumb:98faf768c7d636709ae6e328e7344366614d68db62dcaacf8737c58278cfa71c" "http://10.20.31.153:8080/jenkins/job/ssssss/disable"

還是報缺少合法的Crumb錯誤,這是因為除了提供使用者名稱、密碼、Crumb之外還需要填寫會話cookie(curl客戶端工具不是瀏覽器不會在請求裡面自動帶著cookie值),再次透過API獲取當前使用者的Crumb。

 curl -s -verbose  -u zmc:123456 http://10.20.31.153:8080/jenkins/crumbIssuer/api/json 

接下來請求頭裡面帶著當前使用者的Crumb值、會話cookie,再次禁用流水線。

curl -X POST -u zmc:123456 --cookie "JSESSIONID=8A381340DD8DFC299F9A88BBE10880E0" -H "Jenkins-Crumb:a849f65adaf2ecf8ecf95bf957c1b1288fe86bf091af85a1e7d17421525bb8fc" "http://10.20.31.153:8080/jenkins/job/test-crumb/disable"

檢視流水線專案狀態,專案已禁用。

另外,我們知道Jenkins API認證方式有使用者名稱/密碼+使用者名稱/Token兩種形式,對於使用者名稱/密碼認證的Jenkins任何導致修改的表單提交或者類似的請求,都需要帶著 Crumb,而對於使用者名稱/API Token的話,即使Jenkins開啟了CSRF保護,也不需要提供Crumb。

示例(啟用流水線專案test-crumb):

透過API Token啟用上面禁用的流水線專案。

curl -X POST -u zmc:116e373b20b15ca5788b2a37044f4cb0b5 "http://10.20.31.153:8080/jenkins/job/test-crumb/enable"

檢視流水線專案狀態,專案已啟用。

注意 1:本文主要講解Jenkins如何使用CrumbIssuer防禦CSRF攻擊,關於API認證如何頒發API Token本文不做講解。

注意 2:Crumb的有效期通常與使用者會話繫結,只要會話有效,Crumb就有效。jenkins本身並沒有直接配置會話超時時間的設定,它依賴於底層的Servlet容器的會話配置,假設底層容器用的Tomcat的話,預設會話有效期是30分鐘。

2.3 禁用跨站請求偽造保護功能(強烈不建議)

向Jenkins傳送HTTP請求的過時外掛可能無法在啟用CSRF保護的情況下工作。在這種情況下,可能需要暫時禁用CSRF保護。要禁用CSRF保護,請設定系統屬性hudson.security.CSRF.GlobalCrumbIssuerConfiguration。啟動時將DISABLE_CSRF_PROTECTION設定為true。

3、總結

  Jenkins的CSRF保護使用Crumb,它由Jenkins建立,併傳送給使用者。任何導致修改的表單提交或者類似的操作,比如:觸發任務或修改構建配置,都需要提供Crumb。Crumb包含標識建立它的使用者的資訊,因此使用其他使用者令牌的提交將被拒絕。所有這些都發生在後臺,除了極少數情況外,沒有明顯的影響,例如,在使用者的會話到期後,他們再次登入。

  發給Jenkins的POST請求通常需要提供Crumb。這也適用於使用使用者名稱和密碼進行身份驗證的指令碼客戶端。由於 Crumb 包含網路會話 ID,客戶端需要執行以下操作:

  • 向 /CrumbIssuer/api 傳送請求,請求Crumb,注意 Set-Cookie響應頭
  • 對於所有後續請求,除了使用者名稱和密碼之外,還提供 Crumb 和會話 cookie。

  另外,我們知道Jenkins API認證方式有使用者名稱/密碼+使用者名稱/Token兩種形式,對於使用者名稱/密碼認證的Jenkins任何導致修改的表單提交或者類似的請求,都需要帶著 Crumb,而對於使用者名稱/API Token的話,即使Jenkins開啟了CSRF保護,也不需要提供Crumb。

主要參考:CSRF(跨站請求偽造)的原理和防禦
主要參考:https://www.jenkins.io/doc/book/security/csrf-protection/
參考:https://segmentfault.com/a/1190000040706914

相關文章