正如我們在這本書中反復指出的, HTTP 的特性讓它不容易被居心叵測的人控制,但是同樣使得讓 http 變得非常安全變得富于挑戰性。現在你知道 Web 應用如何優雅的解決了 HTTP 無狀態的問題,但是也可想而知還是有一些安全問題需要注意。舉個例子,如果有人把我瀏覽器里的會話 id 偷走,他是不是就能用我身份登陸了?或者當我訪問別的網站的時候,它們會不會窺視我 Reddit 和 Facebook 的 cookie 里存儲的一些信息,比如會話 id ?本章我們就來討論一下常見的 HTTP 安全問題。
## 安全的 HTTP(HTTPS)
在客戶端和服務器互相發送請求和響應的時候,所有的請求和響應里的信息都是通過明文字符串發送的。如果一個惡意的黑客連接到同一網絡,他就可以利用數據包嗅探技術來讀取來回發送的消息。正如我們已知道的,請求可以包含會話 id ,它唯一地標識你到服務器之間的聯系,所以如果別人復制了這個會話 id ,他們可以手動創建到服務器的請求,偽裝成你的客戶端,甚至都不需要你的用戶名和密碼就可以自動登陸。
這種情況就需要安全的 HTTP 也就是 HTTPS 來幫忙啦。通過 HTTPS 訪問資源的時候,通常以?`https://`?開頭而不是?`http://`?,而且通常在邊上都會有個小鎖子的圖標:
通過 HTTPS 發送的請求和響應在發送前都會被加密。這意味著如果一個惡意的黑客監聽 HTTP 通信,他得到的信息都是加密的和無用的。HTTPS 通過一個叫做?[TLS](http://en.wikipedia.org/wiki/Transport_Layer_Security)?的加密協議來加密消息。在 TLS 開發完成前,早期 HTTPS 使用?`SSL`?( Secure Sockets Layer )。這些加密協議在加密數據之前,需要先使用證書來與遠程服務器進行通信來交換安全密鑰。你可以點擊?`https://`?前面那個小鎖子的圖標來查看這些證書:
在與一個網站進行交互之前,大多數現代瀏覽器都會替你對網站的證書做一些檢查,但是有時候自己手動查看一下證書也可以作為一個額外的安全保障。
## 同源策略( Same-origin policy )
同源策略是一個重要的概念,它允許來自同一站點的資源進行互相訪問而不受限制,但是會阻止其他不同站點對文檔/資源的訪問。換句話說它可以阻止另一個站點通過腳本來操縱本站點的文檔。同源的文檔必須有相同的協議,主機名和端口號。舉個例子,`http://www.test.com/aboutus.html`?上的 HTML 文檔可以嵌入`http://www.test.com/fancy.js`?這個 javascript 文件,因為它們是同源的,有相同的協議,主機名和端口號(默認的 80) 。
反過來說,這就意味著?`http://www.test.com`?上的文檔不能嵌入?`http://www.example.com`?上的文檔,因為它們不是同源的。
> 同源策略涉及的是訪問文件內容,而不是鏈接,你可以隨意鏈接到任何 URL。
雖然這樣很安全,但是有時 web 開發人員需要進行跨域的內容訪問就會很麻煩,所以就有了跨域資源共享技術 CORS 。 CORS 是一種機制,允許我們繞過同源策略,從一個域名向另一個域名的資源發起請求。CORS 的原理是添加新的 HTTP 頭部,來對一些域名授權,那這些域名就可以發起對本頁面資源的請求。
同源策略是防范會話劫持(見下一節)的重要手段,并作為 web 應用安全的基石。下面讓我們來看一些 HTTP 的安全威脅和其對應的防范措施。
## 會話劫持( Session Hijacking )
我們已經知道,會話在維持 HTTP 的狀態上扮演著重要的角色。我們也知道會話 id 作為一個唯一的令牌來唯一標識一個會話。通常,會話 id 是作為 cookie 存儲在計算機上的一個隨機字符串. 會話 id 隨著每一個到服務器的請求被送往服務器用于唯一標識這個會話。事實上,這也就是很多 web 應用的用戶認證系統所在做的事情,當用戶的用戶名和密碼匹配之后,會話 id 會存儲在用戶的瀏覽器里,這樣他們的下一個請求就不用重新認證了。
不幸的是,如果一個攻擊者拿到了這個會話 id ,他就會跟用戶共享這一個會話,同時也就能訪問這個 web 應用了。在會話劫持攻擊中,用戶根本意識不到一個攻擊者甚至不用知道她的用戶名和密碼就可以訪問她的會話了。
### 會話劫持的對策
* 一個比較流行的會話劫持防范措施是重置會話。也就是對于一個用戶認證系統來說,一次成功的登錄包括驗證舊的會話 id 和生成一個新的會話 id 。完成此步驟后,在下一個請求里,會要求受害者進行身份驗證。然后會話 id 就會改變,這樣攻擊者就無法訪問到這個會話了。很多網站都采取這種辦法,當用戶在進行敏感操作的時候保證用戶身份的正確性,比如給信用卡充值或者刪除賬戶的時候。
* 另一個很有用的方法是給會話設置過期時間。那些不會過期的會話給了攻擊者太多的時間去偽裝成一個合法用戶。如果設置了過期時間,比如 30 分鐘,這樣一來攻擊者就不會那么從容的進行攻擊了。
* 最后,其實我們已經講過了,另一個辦法就是整站使用 HTTPS 把攻擊者能得到會話 id 的可能性降至最低。
## 跨站腳本攻擊 (XSS)
我們最后要討論的這個安全問題,對所有 web 開發者來說都很重要,叫做跨站腳本攻擊或者?**XSS**?。當你允許用戶輸入的 HTML 和 javascript 在你自己的網站上直接顯示的時候,就有可能遭受這種攻擊。
舉個例子,下面這個表格允許你輸入評論,然后把評論直接顯示在網頁上:
因為這是一個普通的 HTML 文本框,所以用戶可以在里面輸入任何東西。也就意味著用戶可以直接輸入原始的 HTML 和 javascript 代碼,并把它提交給服務器:
如果服務器端對于用戶的輸入不做任何無害處理的話,這些內容就會注入到網頁的內容中去,然后瀏覽器就會解釋執行這些 HTML 和 javascript 代碼。在本例中會彈出一個警告框,這當然不是我們想要的結果。惡意用戶可以使用 HTML 和 javascript 代碼對服務器或者以后訪問這個頁面的用戶發起毀滅性的攻擊。舉個例子,一個攻擊者可以使用 javascript 代碼去獲取所有在他之后訪問這個頁面的用戶的會話 id ,然后偽裝成其他用戶。而這一切都發生在受害者一無所知的情況下。而且要注意的是,這種攻擊也能繞過同源策略,因為這段惡意代碼是存在于當前這個網站上的。
### 跨站腳本攻擊的解決方案
* 阻止此類攻擊的一個辦法就是總是對用戶輸入的內容做無害處理。消除有問題的輸入,比如``標簽,或者使用一個更安全的輸入格式,比如 Markdown,這樣就可以阻止 HTML 和 javascript 同時出現在用戶的輸入里。
* 第二個辦法就是在顯示之前轉義用戶輸入的所有數據.如果你需要用戶能夠輸入 HTML 和 javascript 代碼,那么當你顯示這些輸入內容的時候要確保它們被正確轉義,這樣瀏覽器就不會把它們當做代碼給執行了。
## 小結
本章,我們討論了 web 應用關于安全的諸多方面。不消說,這是一個寬泛的話題,我們只是蜻蜓點水一般掠過幾個常見問題而已。本章的主要目的是展示一下在 HTTP 之上開發出的 web 應用是多么的脆弱,和確保一個 web 應用的安全性是多么的困難。