一、Http協議無狀態
HTTP是一個基於TCP/IP通信協議來傳遞數據的。它是無連接和無狀態的。
無連接:限制每次連接只處理一個請求。服務器處理完客戶端的請求,並收到客戶端的應答后,斷開連接。采用這種方式可以節省傳輸時間。
無狀態:協議對於客戶端請求的處理沒有記憶能力。也就是說這一次請求和上一次請求是沒有任何聯系的。
這種無狀態的的好處是快速。但無狀態意味着如果后續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。
二、Session 與 Token
1、Session機制
由於http協議的無狀態,那就是必須管理會話,服務器必須記住哪些用戶登錄了系統,哪些用戶往自己的購物車中放了商品,也就是說服務器必須把每客戶端區分開。
於是就誕生了session,服務器給客戶端發一個會話標識(session id), 也就是一個隨機的字符串,每個客戶端獲取的都不一樣,每次客戶端向服務器發起HTTP請求的時候,把這個字符串一並帶上,這樣服務器就能區客戶端的身份了。
不同的用戶訪問服務端的時候會在session對象中存儲鍵值對,“鍵”用來存儲開啟這個用戶信息的“鑰匙”,在登錄成功后,“鑰匙”通過cookie返回給客戶端,客戶端存儲為sessionId記錄在cookie中。當客戶端再次訪問時,會默認帶上cookie中的sessionId來實現會話機制。服務器創建session出來后,會把session的id號,以cookie的形式回寫給客戶機。這樣,只要客戶機的瀏覽器不關,再去訪問服務器時,都會帶着session的id號去,服務器發現客戶機瀏覽器帶session id過來了,就會使用內存中與之對應的session為之服務。
客戶端訪問服務器的流程如下:
1)客戶端會發送一個http請求到服務器端;
2)服務器端接受客戶端請求后,建立一個session,並發送一個http響應到客戶端,這個響應頭,其中就包含Set-Cookie頭部。該頭部包含了sessionId。Set-Cookie格式如下,具體請看Cookie詳解
Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]
3)在客戶端發起的第二次請求,假如服務器給了set-Cookie,瀏覽器會自動在請求頭中添加cookie;
4)服務器接收請求,解析cookie,驗證session id信息,核對成功后返回response給客戶端;
2、Token機制
我們知道session是有狀態的,一般存於服務器內存或硬盤中,當服務器采用分布式或集群時,session就會面對負載均衡問題。
當多個服務器集群時,單台服務器是不能區分當前用戶是否登錄的,因為服務器之間不共享session。當然,我們可以把session集中保存在一台服務器上來解決,但是就不能完全達到負載均衡的效果。於是產生了token機制。
客戶端登陸服務器時,服務端驗證用戶身份合法后收到后生成一個token傳給客戶端,客戶端通過cookie、sessionStorage、localStorage存儲token,再次請求時不會默認攜帶,需要在請求頭中添加認證字段Authorization攜帶token信息,服務器端收到請求后解析請求頭,再通過token信息查找用戶登錄狀態。
token也稱作令牌,通常由uid+time+sign[+固定參數]組成。
token的認證方式類似於臨時的證書簽名, 並且是一種服務端無狀態的認證方式(所謂無狀態就是服務端並不會保存身份認證相關的數據), 非常適合於前后端分離項目的REST API的應用場景。
token 的認證流程與session機制很相似:
1)用戶登錄,成功后服務器返回token給客戶端;
2)客戶端收到token后保存在客戶端;
3)客戶端再次訪問服務器,將token放入http headers中;
4)服務器端采用filter過濾器對token進行校驗。校驗成功則返回請求數據,校驗失敗則返回錯誤碼;
三、CSRF攻擊
1、CSRF攻擊概念:
CSRF跨站點請求偽造(Cross—Site Request Forgery),是攻擊者通過一些技術手段欺騙用戶的瀏覽器去訪問一個自己曾經認證過的網站並執行一定的操作。因為瀏覽器認證過,所以網站會認為是真正的用戶在操作。
攻擊者盜用了你的身份,以你的名義發送惡意請求,對服務器來說這個請求是完全合法的,但是卻完成了攻擊者所期望的一個操作,比如以你的名義發送郵件、發消息,盜取你的賬號,添加系統管理員,甚至於購買商品、虛擬貨幣轉賬等。
2、CSRF攻擊原理:
如下,其中Web A為存在CSRF漏洞的網站,Web B為攻擊者構建的惡意網站,User C為Web A網站的合法用戶。
1)用戶C打開瀏覽器,訪問受信任網站A,輸入用戶名和密碼請求登錄網站A;
2)在用戶信息通過驗證后,網站A產生Cookie信息並返回給瀏覽器,此時用戶登錄網站A成功,可以正常發送請求到網站A;
3)用戶未退出網站A之前,在同一瀏覽器中,打開一個TAB頁訪問網站B;
4)網站B接收到用戶請求后,返回一些攻擊性代碼,並發出一個請求要求訪問第三方站點A;
5)瀏覽器在接收到這些攻擊性代碼后,根據網站B的請求,在用戶不知情的情況下攜帶Cookie信息,向網站A發出請求。網站A並不知道該請求其實是由B發起的,所以會根據用戶C的Cookie信息以C的權限處理該請求,導致來自網站B的惡意代碼被執行。
3、CSRF攻擊實例:
受害者Bob在銀行有一筆存款,通過對銀行的網站發送請求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可以使 Bob 把 1000000 的存款轉到 bob2 的賬號下。通常情況下,該請求發送到網站后,服務器會先驗證該請求是否來自一個合法的session,並且該session的用戶Bob已經成功登陸。
黑客 Mallory 自己在該銀行也有賬戶,他知道上文中的 URL 可以把錢進行轉帳操作。Mallory 可以自己發送一個請求給銀行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是這個請求來自 Mallory 而非 Bob,他不能通過安全認證,因此該請求不會起作用。
這時,Mallory 想到使用 CSRF 的攻擊方式,他先自己做一個網站,在網站中放入如下代碼:src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory ”,並且通過廣告等誘使 Bob 來訪問他的網站。當 Bob 訪問該網站時,上述 url 就會從 Bob 的瀏覽器發向銀行,而這個請求會附帶 Bob 瀏覽器中的 cookie 一起發向銀行服務器。大多數情況下,該請求會失敗,因為他要求 Bob 的認證信息。但是,如果 Bob 當時恰巧剛訪問他的銀行后不久,他的瀏覽器與銀行網站之間的 session 尚未過期,瀏覽器的 cookie 之中含有 Bob 的認證信息。這時,悲劇發生了,這個 url 請求就會得到響應,錢將從 Bob 的賬號轉移到 Mallory 的賬號,而 Bob 當時毫不知情。等以后 Bob 發現賬戶錢少了,即使他去銀行查詢日志,他也只能發現確實有一個來自於他本人的合法請求轉移了資金,沒有任何被攻擊的痕跡。而 Mallory 則可以拿到錢后逍遙法外。
4、CSRF漏洞檢測:
檢測CSRF漏洞是一項比較繁瑣的工作,最簡單的方法就是抓取一個正常請求的數據包,去掉Referer字段后再重新提交,如果該提交還有效,那么基本上可以確定存在CSRF漏洞。
隨着對CSRF漏洞研究的不斷深入,不斷涌現出一些專門針對CSRF漏洞進行檢測的工具,如CSRFTester,CSRF Request Builder等。
以CSRFTester工具為例,CSRF漏洞檢測工具的測試原理如下:使用CSRFTester進行測試時,首先需要抓取我們在瀏覽器中訪問過的所有鏈接以及所有的表單等信息,然后通過在CSRFTester中修改相應的表單等信息,重新提交,這相當於一次偽造客戶端請求。如果修改后的測試請求成功被網站服務器接受,則說明存在CSRF漏洞,當然此款工具也可以被用來進行CSRF攻擊。
5、CSRF漏洞防御:
1)檢查referer字段
HTTP頭中有一個Referer字段,這個字段是用來標明請求來源於哪一個網址。通常來說,Referer字段應和請求的地址是在同一個域名下的。服務器可以通過判斷Referer字段來判斷請求的來源。
這種方法簡單易行,但也有其局限性。http協議無法保證來訪的瀏覽器的具體實現,可以通過篡改Referer字段的方式來進行攻擊。
2)使用token
CSRF 攻擊之所以能夠成功,是因為黑客可以完全偽造用戶的請求,該請求中所有的用戶驗證信息都是存在於 cookie 中,因此黑客可以在不知道這些驗證信息的情況下直接利用用戶自己的 cookie 來通過安全驗證。要抵御 CSRF,關鍵在於在請求中放入黑客所不能偽造的信息,並且該信息不存在於 cookie 之中。可以在 HTTP 請求中以參數的形式加入一個隨機產生的 token,並在服務器端建立一個攔截器來驗證這個 token,如果請求中沒有 token 或者 token 內容不正確,則認為可能是 CSRF 攻擊而拒絕該請求。
這種方法要比檢查 Referer 要安全一些,token可以在用戶登陸后產生並放於session之中,然后在每次請求時把token從session中拿出,與請求中的 token 進行比對,但這種方法的難點在於如何把 token 以參數的形式加入請求。