再談 Cookie 和 Session 安全性
最近整理一些關於業務安全的東西,然後又遇到了這個問題,雖然自己每次提到這個問題第一反應都是一個是在伺服器端儲存另一個是在客戶端儲存,但我知道這並不是正確的答案,因為 session 也需要在客戶端儲存一個識別符號 session_id,所以還是想再寫一下這個問題。
0X01 Cookie 與 Session 的博弈
注意:
攻擊者有兩種方式獲取 cookie ,一種是通過中間人攻擊,一種是利用 XSS 這裡我們就不討論中間人攻擊對兩者的影響,因為這個可以使用 SSL 進行傳輸防止中間人攻擊,設定 cookie 的時候只要加一個ecure 選項就可以保證只在 https 的情況下傳輸 cookie 了。
1.能否防止重放攻擊
還是回到剛剛說的那個問題,如果說 session 是儲存在伺服器端的 cookie 是儲存在客戶端是兩者的最大區別的話,我不反對,但是說這是他們安全性不同的原因的話,我認為是非常片面的
熟悉 XSS 的人都知道,在 cookie 沒有設定 http-only 的情況下獲取 cookie 的值是非常輕鬆的,這也是人們常說的 cookie 是不安全的原因之一,這固然不可否認。
但是你要知道 session 他也並不是完全的就放在伺服器端而和客戶端沒有半毛錢關係,想想也不可能,要不伺服器端怎麼確定客戶端是誰啊,所以他必須在客戶端放一個識別符號 session-id,我們可以理解為 伺服器端 session 資料庫的一個索引,客戶端每次上交這個識別符號給伺服器端然後伺服器端根據這個標識在資料庫中進行檢索,從而判定使用者的身份。
所以,從這一點來看,cookie 和 session 沒什麼本質的區別,session_id 也是放在 cookie 中的,他們都不能防止被竊取從而進行重放攻擊。
2.能否防止篡改攻擊
session 設定了以後儲存在伺服器端,客戶端看到的只是一個session_id,這就相當於起到了一個隱藏或者說加密的作用
比如說你設定
$SESSION['user']='admin';
這個返回的header就是set-cookie: sessionId:xxxxxxx(一堆加密了的字元)
但是你設定 cookie 他預設就不會加密,設定啥就顯示啥
set_cooke('user', 'xiaoming');
它返回的header就是 set-cookie: user:xiaoming
那麼這一點就可以作為兩者安全性的一個重要指標,這就可以防止會話的偽造攻擊,攻擊者即使是獲取了整個 cookie 的資料(這裡特指裡面的 session_id,至於cookie 中除了 session_id 以外的其他內容是一些臨時的資料這裡不做討論)但是攻擊者並不能理解 session_id 的內容,最多隻能是利用這個session_id 進行偽造,但是不能篡改 session_id 進行越權(包括水平越權和垂直越權),而單純使用 cookie 就不一樣了 ,你看我上面這個 Cookie就是明文的,什麼資訊在裡面一清二楚。如果攻擊者拿到了這個 cookie 的話,那麼就很容易聯想到可以將 xiaoming 改成 admin 這類的,從而進行越權。
3.新的思考
那有人說了,既然這樣我們程式碼裡對這個 cookie 加密一下不就完了,不是起到了一樣的效果?就像下面這個樣子
set_cookie('user', some_encrypt('xiaoming', 'privatekey','timestamp'));
沒錯,這樣確實起到了防止 cookie 偽造的作用,那這樣看 session 還有什麼用呢?為什麼不是選擇僅僅加密一下 cookie 作為防止攻擊的手段,而要費盡心思的設計這個 session呢?session 的優勢在這裡又體現在什麼地方呢?
原因大致有以下兩點:
(1)開發的方便性和系統的安全性
在實際開發的時候,session的實現和應用已經很成熟。可以將其做分散式的儲存和共享,它的加密演算法也無需再去進行更改或者什麼的,這樣就不需要每個開發者自己思考用什麼演算法,這樣會導致很不統一,並且也增加了開發人員的負擔,同時這也避免了開發者自己去設計一些亂七八糟的演算法,不合理的設計和使用反而會降低系統的安全性。
(2)資料包的大小
我們知道使用者的資訊還是比較多的,如果我們使用加密演算法在本地進行加密傳輸的話,加密後資料會膨脹的很大,這樣就會增加資料包的大小,這是很不好的。
綜上,使用 session 在安全性和實用性上要比單純使用 cookie 儲存使用者資訊更優越
0X02 常見問題
我在網站尋找類似的文章的時候看到一些結論,有些是比較片面的,或者說只是其中的一種情況而已,下面來糾正一下。
問題一:cookie/session_id 在瀏覽器關閉後就失效了
首先這句話肯定是錯的,因為這需要一個前提條件, 條件就是:預設情況下 ,我們知道 cookie 在設定的時候是可以設定生存時間的,就像下面這個樣子
setcookie('sessid', 'uniqid', time()+3600, '/', '', true, true);
我們可以將其設定為很長一段時間,那麼這樣的話,cookie 或者說 session_id 就會被瀏覽器儲存在硬盤裡長期有效。
補充:
session 的預設生存時間是 20 分鐘,也就是說你不另外設定的話 session_id 20分鐘後就失效了
問題二:cookie分為二種,以檔案方式存在硬碟空間上的長期性的cookie和停留在瀏覽器所佔記憶體中的臨時性的cookie
這句話其實還是片面的,只能說從時間上劃分可以這樣將 cookie 分成兩類,但是他後面這麼說(我這裡就直接截圖了):
我是在是不懂這是在說啥,巨大的誤導……他似乎把 cookie 和 session_id 完全分開了,其實cookie 中真正儲存使用者資訊的就是 session_id
我們選擇記住自己的登入狀態其實就是設定比較長的 cookie 或者說 session_id 的生存時間,然後讓其儲存在客戶端的硬碟上。
0X03 參考連結
http://www.cnblogs.com/demingblog/p/3878185.html
0X04 總結
這篇文章其實是我一直以來都想寫的,但是由於時間關係以及自己的理解還不是特別的到位,不知道該怎麼表達,今天因為又遇到了這個問題,我就又藉此機會好好的思考了一下,也請教了我的一些專業的開發的學長,寫下這篇文章,也是希望讓更多的和我曾經一樣對此困惑的人有一個參考,當然我依然不能保證我說的完全正確,我希望更多專業的人能幫助我發現這篇文章中的問題並聯系我加以糾正,以免誤導更多的人。