理解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是如何工作的呢?
- 服務器發(fā)送給客戶端一個token。
- 客戶端提交的表單中帶著這個token。
- 如果這個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是否是合法的。
因此:
- 一般認為安全加密的CSRF tokens是防護CSRF的關鍵
- 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)的時候。