理解CSRF(跨站請求偽造)

理解CSRF(跨站請求偽造)

原文出處Understanding CSRF

對于Express團隊的csrf模塊和csurf模塊的加密函數(shù)的用法我們經(jīng)常有一些在意。
這些在意是莫須有的,因為他們不了解CSRF token是如何工作的。
下面快速過一遍!

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

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

在他們的釣魚站點,攻擊者可以通過創(chuàng)建一個AJAX按鈕或者表單來針對你的網(wǎng)站創(chuàng)建一個請求:

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

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

如果減輕CSRF攻擊?

只使用JSON api

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

禁用CORS

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

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

檢驗referrer頭部

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

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

GET總是冪等的

確保你的GET請求不會修改你數(shù)據(jù)庫中的相關數(shù)據(jù)。
這是一個初學者常犯的錯誤,使得你的應用不僅是易于遭受CSRF攻擊。

避免使用POST

因為<form>只能用GET或是POST,
而不能使用別的方法,例如PUT, PATCH, DELETE
攻擊者很難有方法攻擊你的網(wǎng)站。

不要復寫方法

許多應用程序使用復寫方法來在一個常規(guī)表單中使用PUT, PATCH, 和DELETE請求。
這會使得原先不易受攻擊的方法變得易受攻擊。

不要兼容舊瀏覽器

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

CSRF Tokens

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

  1. 服務器發(fā)送給客戶端一個token。
  2. 客戶端提交的表單中帶著這個token。
  3. 如果這個token不合法,那么服務器拒絕這個請求。

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

確保CSRF token不能通過AJAX訪問到!
不要創(chuàng)建一個/CSRF路由來獲取一個token,
尤其不要在這個路由上支持CORS!

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

BREACH攻擊

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

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

了解更多:

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

salt不需要加密

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

創(chuàng)建tokens必須要快

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

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

如果你正在使用一個數(shù)據(jù)庫后端來存儲session,客戶端是不會知道秘鑰的,因為它被存儲在數(shù)據(jù)庫中。
如果你正在使用cookie來存儲session,那么秘鑰就會被存儲在cookie中發(fā)送給客戶端。
因此, 確保cookie sessions 使用 httpOnly 那樣客戶端就不能通過客戶端JavaScript來讀取到秘鑰!

當你不正確的使用CSRF token

把它們加到JSON AJAX調用中

正如上面提到的,如果你不支持CORS并且你的API是傳輸?shù)膰栏竦腏SON,
絕沒可能在你的AJAX 調用中加入CSRF token。

通過AJAX暴露你的CSRF token

不要創(chuàng)建一個GET /csrf路由
并且尤其不要在這個路由上支持CORS。
不要發(fā)送CSRF token在API響應的body中。

結論

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容