CSRF,是跨站請求僞造(Cross Site Request Forgery)的縮寫,是一種劫持受信任用戶向服務器發送非預期請求的攻擊方式。php
一般狀況下,CSRF 攻擊是攻擊者藉助受害者的 Cookie 騙取服務器的信任,在受害者絕不知情的狀況下以受害者名義僞造請求發送給受攻擊服務器,從而在並未受權的狀況下執行在權限保護之下的操做。html
這裏有一個網站,用戶能夠看文章,登陸以後能夠發評論。前端
若是用戶是登陸狀態,打開了這樣的頁面,web
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>csrf攻擊</title> <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" /> </head> <body style="display: none;"> <form target="myIframe" id="csrf" action="https://www.kkkk1000.com/csrf/data/post_comment.php" metdod="POST"> <input type="text" name="content" value="csrf攻擊" /> </form> <!-- iframe 用來防止頁面跳轉 --> <iframe id="myIframe" name="myIframe"></iframe> <script> var form = document.querySelector('#csrf'); form.submit(); </script> </body> </html>
就會自動在文章下發一條評論,這樣就算完成了一次 CSRF 攻擊。
固然,若是你把這個頁面放服務器上,而後作成一個連接,用戶點擊這個連接,一樣能夠完成攻擊。算法
從圖中能夠看出,右邊和左邊的頁面是在不一樣站點下的,用戶打開的右邊的空白頁,就偷偷提交了一條評論,刷新左邊的頁面也確實看到了剛剛提交的評論。segmentfault
咱們來看看,此次的攻擊是怎麼成功的。後端
首先咱們要知道一些關於 Cookie 的知識。瀏覽器
HTTP Cookie(也叫 Web Cookie或瀏覽器 Cookie)是服務器發送到用戶瀏覽器並保存在本地的一小塊數據,它會在瀏覽器下次向同一服務器再發起請求時被攜帶併發送到服務器上。一般,它用於告知服務端兩個請求是否來自同一瀏覽器,如保持用戶的登陸狀態。Cookie 使基於無狀態的 HTTP 協議記錄穩定的狀態信息成爲了可能。
好的,咱們繼續往下說。安全
由於用戶在網站登陸後,這個網站服務端會在 Cookie 中會放一個憑證,這個憑證是後端用來驗證用戶身份的。
在發評論的時候,提交評論的請求會帶上這個憑證,後端經過判斷這個憑證,來確認是哪一個用戶。服務器
登陸時,設置 Cookie:
提交評論時,攜帶 Cookie:
咱們再來看看,發起攻擊的頁面裏的內容。
<body style="display: none;"> <form target="myIframe" id="csrf" action="https://www.kkkk1000.com/csrf/data/post_comment.php" metdod="POST"> <input type="text" name="content" value="csrf攻擊" /> </form> <!-- iframe 用來防止頁面跳轉 --> <iframe id="myIframe" name="myIframe"></iframe> <script> var form = document.querySelector('#csrf'); form.submit(); </script> </body>
這些代碼的意思就是,當用戶點擊這個連接,會自動提交 form 表單,而這個表單就是用來提交評論的,提交評論請求須要的參數,在 form 表單中也都已經準備好了,若是用戶登陸過網站,Cookie 中存儲的用戶的憑證,會隨着請求一塊兒傳到服務器端。因此服務器端就會認爲這是用戶要提交一條評論。
Cookie 的 SameSite 屬性用來限制第三方 Cookie,從而減小安全風險,能夠用來防止 CSRF 攻擊和用戶追蹤。
它能夠設置三個值。
Strict最爲嚴格,徹底禁止第三方 Cookie,跨站點時,任何狀況下都不會發送 Cookie。換言之,只有當前網頁的 URL 與請求目標一致,纔會帶上 Cookie。
Set-Cookie: CookieName=CookieValue; SameSite=Strict;
這個規則過於嚴格,可能形成很是很差的用戶體驗。好比,當前網頁有一個 GitHub 連接,用戶點擊跳轉就不會帶有 GitHub 的 Cookie,跳轉過去老是未登錄狀態。
Lax 規則稍稍放寬,大多數狀況也是不發送第三方 Cookie,可是導航到目標網址的 Get 請求除外。
Set-Cookie: CookieName=CookieValue; SameSite=Lax;
Chrome 計劃將 Lax 變爲默認設置。這時,網站能夠選擇顯式關閉 SameSite 屬性,將其設爲 None 。不過,前提是必須同時設置 Secure 屬性(Cookie 只能經過 HTTPS 協議發送),不然無效。
下面的設置無效:
Set-Cookie: widget_session=abc123; SameSite=None
下面的設置有效:
Set-Cookie: widget_session=abc123; SameSite=None; Secure
要使用 SameSite 屬性,前提是用戶瀏覽器支持 SameSite 屬性,可使用 caniuse 查看瀏覽器對於 SameSite 屬性的支持。
在 HTTP 協議中,每個異步請求都會攜帶兩個 Header ,用於標記來源域名:
這兩個 Header 在瀏覽器發起請求時,大多數狀況會自動帶上,而且不能由前端自定義內容。 服務器能夠經過解析這兩個 Header 中的域名,肯定請求的來源域。
經過校驗請求的該字段,咱們能知道請求是不是從本站發出的。咱們能夠經過拒絕非本站發出的請求,來避免了 CSRF 攻擊。
若是 Origin 存在,那麼直接使用 Origin 中的字段確認來源域名就能夠。
可是 Origin 在如下兩種狀況下並不存在:
若是 Referer 存在,也能夠用來確認 HTTP 請求的來源地址。
須要注意的是在如下狀況下 Referer 沒有或者不可信:
window.location.href=url
進行界面的跳轉,會丟失 Referer。window.open
,也會缺失 Referer。總的來講,同源檢測是一個相對簡單的防範方法,可以防範絕大多數的 CSRF 攻擊,但這並非萬無一失的,對於安全性要求較高,或者有較多用戶輸入內容的網站,咱們就要對關鍵的接口作額外的防禦措施。
驗證碼的種類有不少,好比圖形驗證碼,基於人機之間知識差別的驗證碼,行爲式驗證碼。
CSRF 攻擊每每是在用戶不知情的狀況下成功僞造請求。而驗證碼會強制用戶必須與應用進行交互,才能完成最終請求,並且由於 CSRF 攻擊沒法獲取到驗證碼,所以一般狀況下,驗證碼可以很好地遏制 CSRF 攻擊。
但驗證碼並非萬能的,由於出於用戶體驗考慮,不能給網站全部的操做都加上驗證碼。所以,驗證碼只能做爲防護 CSRF 的一種輔助手段,而不能做爲最主要的解決方案。
CSRF 攻擊之因此可以成功,是由於攻擊者能夠徹底僞造用戶的請求,該請求中全部的用戶驗證信息都是存在於 Cookie 中,所以攻擊者能夠在不知道這些驗證信息的狀況下直接利用用戶本身的 Cookie 來經過安全驗證。
要抵禦 CSRF,關鍵在於在請求中放入攻擊者所不能僞造的信息,而且該信息不存在於 Cookie 之中。能夠在 HTTP 請求中以參數的形式加入一個隨機產生的 Token,並在服務器端創建一個攔截器來驗證這個 Token,若是請求中沒有 Token 或者 Token 內容不正確,則認爲多是 CSRF 攻擊而拒絕該請求。
一、服務器將 Token 返回到前端
用戶打開頁面時,前端發起請求,服務器會返回一個 Token,該 Token 經過加密算法對數據進行加密,通常 Token 都包括隨機字符串和時間戳的組合,同時 Token 會存在服務器的 Session 中。以後頁面加載完成時,使用 JS 遍歷整個 DOM 樹,在 DOM 中全部地址是本站的 a
和 form
標籤中加入 Token,其餘的請求就在編碼時手動添加 Token 這個參數。
二、前端發請求時攜帶這個 Token
對於 GET 請求,Token 將附在請求地址以後,這樣 URL 就變成 http://url?token=tokenvalue
。 而對於 form
標籤發起的 POST 請求來講,要在 form
的最後加上:
<input type=」hidden」 name=」token」 value=」tokenvalue」/>
總之,就是前端發請求時把 Token 以參數的形式加入請求中。
三、服務器驗證 Token 是否正確
當前端獲得了 Token ,再次提交給服務器的時候,服務器須要判斷 Token 的有效性,驗證過程是先解密 Token,對比加密字符串以及時間戳,若是加密字符串一致且時間未過時,那麼這個 Token 就是有效的。
CSRF可以攻擊成功的本質是:重要操做的全部參數都是能夠被攻擊者猜想到的。
因此只要防止攻擊者成功的構造一個僞造請求,就能夠杜絕攻擊了!