理解CSRF(跨站請求偽造)

苟哥發表於2016-03-27


原文出處Understanding CSRF

對於Express團隊的csrf模組和csurf模組的加密函式的用法我們經常有一些在意。
這些在意是莫須有的,因為他們不瞭解CSRF token是如何工作的。 下面快速過一遍!

讀過後還有疑問?希望告訴我們錯誤?請開一個issue!

一個CSRF攻擊是如何工作的?

在他們的釣魚站點,攻擊者可以通過建立一個AJAX按鈕或者表單來針對你的網站建立一個請求:

<form action="https://my.site.com/me/something-destructive" method="POST">
  <button type="submit">Click here for free money!</button>
</form>

這是很危險的,因為攻擊者可以使用其他http方法例如 delete 來獲取結果。 這在使用者的session中有很多關於你的網站的詳細資訊時是相當危險的。 如果一個不懂技術的使用者遇到了,他們就有可能會輸入信用卡號或者個人安全資訊。

如果減輕CSRF攻擊?

只使用JSON
api

使用JavaScript發起AJAX請求是限制跨域的。 不能通過一個簡單的<form>來傳送JSON
所以,通過只接收JSON,你可以降低發生上面那種情況的可能性。

禁用CORS

第一種減輕CSRF攻擊的方法是禁用cross-origin requests(跨域請求)。 如果你希望允許跨域請求,那麼請只允許 OPTIONS, HEAD, GET 方法,因為他們沒有副作用。

不幸的是,這不會阻止上面的請求由於它沒有使用JavaScript(因此CORS不適用)。

檢驗referrer頭部

不幸的是,檢驗referrer頭部很麻煩, 但是你可以阻止那些referrer頭部不是來自你的頁面的請求。 這實在不值得麻煩。

舉個例子,你不能載入session如果這個請求的referrer頭部不是你的伺服器。

GET總是冪等的

確保你的GET請求不會修改你資料庫中的相關資料。 這是一個初學者常犯的錯誤,使得你的應用不僅是易於遭受CSRF攻擊。

避免使用POST

因為<form>只能用GET或是POST,
而不能使用別的方法,例如PUTPATCHDELETE
攻擊者很難有方法攻擊你的網站。

不要複寫方法

許多應用程式使用複寫方法來在一個常規表單中使用PUTPATCH,
DELETE請求。 這會使得原先不易受攻擊的方法變得易受攻擊。

不要相容舊瀏覽器

舊的瀏覽器不支援CORS或是其他安全政策。 通過不相容舊瀏覽器 (那些不懂技術的人用的越多,我們越容易被攻擊), 你可以最小化受到攻擊的可能性。

CSRF
Tokens

最終的解決辦法是使用CSRF tokens。 CSRF tokens是如何工作的呢?

  1. 伺服器傳送給客戶端一個token。
  2. 客戶端提交的表單中帶著這個token。
  3. 如果這個token不合法,那麼伺服器拒絕這個請求。

攻擊者需要通過某種手段獲取你站點的CSRF token, 他們只能使用JavaScript來做。 所以,如果你的站點不支援CORS, 那麼他們就沒有辦法來獲取CSRF token, 降低了威脅。

確保CSRF token不能通過AJAX訪問到! 不要建立一個/CSRF路由來獲取一個token, 尤其不要在這個路由上支援CORS!

token需要是不容易被猜到的, 讓它很難被攻擊者嘗試幾次得到。 它不需要是密碼安全的。 攻擊來自從一個未知的使用者的一次或者兩次的點選, 而不是來自一臺伺服器的暴力攻擊。

BREACH攻擊

這也就是salt(加鹽)出現的原因。 Breach攻擊相當簡單:如果伺服器通過HTTPS+gzip多次傳送相同或者相似的響應,攻擊者就可以猜測響應的內容(使得HTTPS完全無用)。 解決辦法?讓每一個響應都有那麼一點不同。 於是,CSRF tokens依據每一個不同的請求還有不同的時間來生成。
但是伺服器需要知道客戶端請求中帶的token是否是合法的。 因此:

  1. 一般認為安全加密的CSRF tokens是防護CSRF的關鍵
  2. CSRF tokens現在通常是一個祕鑰或者salt的hash

瞭解更多:

注意,CSRF沒有_解決_BREACH攻擊, 但是這個模組通過隨機化請求來為你減輕BREACH攻擊。

salt不需要加密

因為客戶端知道salt!!! 伺服器會傳送 <salt>;<token> ,然後客戶端會通過請求返回相同的值給伺服器。伺服器然後會檢驗<secret>+<salt>=<token> 。
salt必須跟token一起被髮送給伺服器,否則伺服器不能驗證這個token。 這是最簡單的加密方式。 還有很多方法,不過他們更加複雜,犯不著那麼麻煩。

建立tokens必須要快

因為每當進來一個請求他們就會被建立! 像Math.random().toString(36).slice(2)這麼做也是效能足夠好的! 你不需要OpenSSL來為每一個請求建立一個密碼安全的token。

祕鑰不需要是加密的,但需要是安全的

如果你正在使用一個資料庫後端來儲存session,客戶端是不會知道祕鑰的,因為它被儲存在資料庫中。 如果你正在使用cookie來儲存session,那麼祕鑰就會被儲存在cookie中傳送給客戶端。 因此, 確保cookie sessions 使用 httpOnly 那樣客戶端就不能通過客戶端JavaScript來讀取到祕鑰!

當你不正確的使用CSRF
token

把它們加到JSON
AJAX呼叫中

正如上面提到的,如果你不支援CORS並且你的API是傳輸的嚴格的JSON, 絕沒可能在你的AJAX 呼叫中加入CSRF token。

通過AJAX暴露你的CSRF
token

不要建立一個GET /csrf路由 並且尤其不要在這個路由上支援CORS。 不要傳送CSRF token在API響應的body中。

結論

因為web正在向JSON API轉移,並且瀏覽器變得更安全,有更多的安全策略, CSRF正在變得不那麼值得關注。 阻止舊的瀏覽器訪問你的站點,並儘可能的將你的API變成JSON API, 然後你將不再需要CSRF token。 但是為了安全起見,你還是應該儘量允許他們尤其是當難以實現的時候。


相關文章