原文地址:http://www.freebuf.com/articles/web/39234.htmljavascript
隨着Web2.0、社交網絡、微博等等一系列新型的互聯網產品的誕生,基於Web環境的互聯網應用愈來愈普遍,企業信息化的過程當中各類應用都架設在Web平臺上,Web業務的迅速發展也引發黑客們的強烈關注,接踵而至的就是Web安全威脅的凸顯。php
黑客利用網站操做系統的漏洞和Web服務程序的SQL注入漏洞等獲得Web服務器的控制權限,輕則篡改網頁內容,重則竊取重要內部數據,更爲嚴重的則是在網頁中植入惡意代碼,使得網站訪問者受到侵害。html
現在,Web安全成爲焦點,但網站的漏洞仍是頻頻出現,在白帽子們進行網站測試時,恐怕對於SQL注入、XSS跨站、CSRF接觸最多,但對於網站的開發者們來講,對這些熟知多少?本文從開發者的角度,對於XSS和CSRF進行簡要概述。java
PART1 XSS跨站腳本(Cross-site scripting)node
XSS成因歸納 :web
XSS其實就是Html的注入問題,攻擊者的輸入沒有通過嚴格的控制進入了數據庫,最終顯示給來訪的用戶,致使能夠在來訪用戶的瀏覽器裏以瀏覽用戶的身份執行Html代碼,數據流程以下:攻擊者的Html輸入—>web程序—>進入數據庫—>web程序—>用戶瀏覽器。ajax
檢測方法:數據庫
//一般有一些方式能夠測試網站是否有正確處理特殊字符:express
><script>alert(document.cookie)</script> ='><script>alert(document.cookie)</script> "><script>alert(document.cookie)</script> <script>alert(document.cookie)</script> <script>alert(vulnerable)</script> %3Cscript%3Ealert('XSS')%3C/script%3E <script>alert('XSS')</script> <img src="javascript:alert('XSS')"> <img src="http://xxx.com/yyy.png" onerror="alert('XSS')"> <div style="height:expression(alert('XSS'),1)" />(這個僅限 IE 有效)
攻擊手段和目的:瀏覽器
攻擊者使被攻擊者在瀏覽器中執行腳本後,若是須要收集來自被攻擊者的數據(如cookie或其餘敏感信息),能夠自行架設一個網站,讓被攻擊者經過JavaScript等方式把收集好的數據做爲參數提交,隨後以數據庫等形式記錄在攻擊者本身的服務器上。
a. 盜用 cookie ,獲取敏感信息。
b.利用植入 Flash ,經過 crossdomain 權限設置進一步獲取更高權限;或者利用Java等獲得相似的操做。
c.利用 iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻擊)用戶的身份執行一些管理動做,或執行一些通常的如發微博、加好友、發私信等操做。
d.利用可被攻擊的域受到其餘域信任的特色,以受信任來源的身份請求一些平時不容許的操做,如進行不當的投票活動。
e.在訪問量極大的一些頁面上的XSS能夠攻擊一些小型網站,實現DDoS攻擊的效果。
漏洞的防護和利用:
避免XSS的方法之一主要是將用戶所提供的內容進行過濾,許多語言都有提供對HTML的過濾:
PHP的htmlentities()或是htmlspecialchars()。
Python的cgi.escape()。
ASP的Server.HTMLEncode()。
ASP.NET的Server.HtmlEncode()或功能更強的Microsoft Anti-Cross Site Scripting Library
Java的xssprotect(Open Source Library)。
Node.js的node-validator。
使用HTTP頭指定類型:
不少時候可使用HTTP頭指定內容的類型,使得輸出的內容避免被做爲HTML解析。如在PHP語言中使用如下代碼:
header
('Content-Type: text/javascript; charset=utf-8');
便可強行指定輸出內容爲文本/JavaScript腳本(順便指定了內容編碼),而非能夠引起攻擊的HTML。
PART2 CSRF:冒充用戶之手
示意圖:
XSS 是實現 CSRF 的諸多途徑中的一條,但絕對不是惟一的一條。通常習慣上把經過 XSS 來實現的 CSRF 稱爲 XSRF。
CSRF 顧名思義,是僞造請求,冒充用戶在站內的正常操做。咱們知道,絕大多數網站是經過 cookie 等方式辨識用戶身份(包括使用服務器端 Session 的網站,由於 Session ID 也是大多保存在 cookie 裏面的),再予以受權的。因此要僞造用戶的正常操做,最好的方法是經過 XSS 或連接欺騙等途徑,讓用戶在本機(即擁有身份 cookie 的瀏覽器端)發起用戶所不知道的請求。
要完成一次CSRF攻擊,受害者必須依次完成兩個步驟:
1.登陸受信任網站A,並在本地生成Cookie。
2.在不登出A的狀況下,訪問危險網站B。
看到這裏,你也許會說:「若是我不知足以上兩個條件中的一個,我就不會受到CSRF的攻擊」。是的,確實如此,但你不能保證如下狀況不會發生:
1.你不能保證你登陸了一個網站後,再也不打開一個tab頁面並訪問另外的網站。
2.你不能保證你關閉瀏覽器了後,你本地的Cookie馬上過時,你上次的會話已經結束。(事實上,關閉瀏覽器不能結束一個會話,但大多數人都會錯誤的認爲關閉瀏覽器就等於退出登陸/結束會話了……)
3.上圖中所謂的攻擊網站,多是一個存在其餘漏洞的可信任的常常被人訪問的網站。
上面大概地講了一下CSRF攻擊的思想,下面我將用幾個例子詳細說說具體的CSRF攻擊,這裏我以一個銀行轉帳的操做做爲例子(僅僅是例子,真實的銀行網站沒這麼傻:>)
示例1:
銀行網站A,它以GET請求來完成銀行轉帳的操做,如:
http://www.mybank.com/Transfer.php?toBankId=11&money=1000
危險網站B,它裏面有一段HTML的代碼以下:
<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>
首先,你登陸了銀行網站A,而後訪問危險網站B,噢,這時你會發現你的銀行帳戶少了1000塊……
爲何會這樣呢?緣由是銀行網站A違反了HTTP規範,使用GET請求更新資源。在訪問危險網站B的以前,你已經登陸了銀行網站A,而B中的<img>以GET的方式請求第三方資源(這裏的第三方就是指銀行網站了,本來這是一個合法的請求,但這裏被不法分子利用了),因此你的瀏覽器會帶上你的銀行網站A的Cookie發出Get請求,去獲取資源「http://www.mybank.com/Transfer.php?toBankId=11&money=1000」,結果銀行網站服務器收到請求後,認爲這是一個更新資源操做(轉帳操做),因此就馬上進行轉帳操做……
示例2:
爲了杜絕上面的問題,銀行決定改用POST請求完成轉帳操做。
銀行網站A的WEB表單以下:
<form action="Transfer.php" method="POST"> <p>ToBankId: <input type="text" name="toBankId" /></p> <p>Money: <input type="text" name="money" /></p> <p><input type="submit" value="Transfer" /></p> </form>
後臺處理頁面Transfer.php以下:
<?php session_start(); if (isset($_REQUEST['toBankId'] && isset($_REQUEST['money'])) { buy_stocks($_REQUEST['toBankId'], $_REQUEST['money']); } >
危險網站B,仍然只是包含那句HTML代碼:
<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>
和示例1中的操做同樣,你首先登陸了銀行網站A,而後訪問危險網站B,結果…..和示例1同樣,你再次沒了1000塊~T_T,此次事故的緣由是:銀行後臺使用了$_REQUEST去獲取請求的數據,而$_REQUEST既能夠獲取GET請求的數據,也能夠獲取POST請求的數據,這就形成了在後臺處理程序沒法區分這究竟是GET請求的數據仍是POST請求的數據。在PHP中,可使用$_GET和$_POST分別獲取GET請求和POST請求的數據。在JAVA中,用於獲取請求數據request同樣存在不能區分GET請求數據和POST數據的問題。
示例3:
通過前面2個慘痛的教訓,銀行決定把獲取請求數據的方法也改了,改用$_POST,只獲取POST請求的數據,後臺處理頁面Transfer.php代碼以下:
<?php session_start(); if (isset($_POST['toBankId'] && isset($_POST['money'])) { buy_stocks($_POST['toBankId'], $_POST['money']); } ?>
然而,危險網站B與時俱進,它改了一下代碼:
<html> <head> <script type="text/javascript"> function steal() { iframe = document.frames["steal"]; iframe.document.Submit("transfer"); } </script> </head> <body onload="steal()"> <iframe name="steal" display="none"> <form method="POST" name="transfer" action="http://www.myBank.com/Transfer.php"> <input type="hidden" name="toBankId" value="11"> <input type="hidden" name="money" value="1000"> </form> </iframe> </body> </html>
若是用戶還是繼續上面的操做,很不幸,結果將會是再次不見1000塊……由於這裏危險網站B暗地裏發送了POST請求到銀行!
總結一下上面3個例子,CSRF主要的攻擊模式基本上是以上的3種,其中以第1,2種最爲嚴重,由於觸發條件很簡單,一個<img>就能夠了,而第3種比較麻煩,須要使用JavaScript,因此使用的機會會比前面的少不少,但不管是哪一種狀況,只要觸發了CSRF攻擊,後果都有可能很嚴重。
理解上面的3種攻擊模式,其實能夠看出,CSRF攻擊是源於WEB的隱式身份驗證機制!WEB的身份驗證機制雖然能夠保證一個請求是來自於某個用戶的瀏覽器,但卻沒法保證該請求是用戶批准發送的!
如何防護?
請求令牌(一種簡單有效的防護方法):
首先服務器端要以某種策略生成隨機字符串,做爲令牌(token),保存在 Session 裏。而後在發出請求的頁面,把該令牌以隱藏域一類的形式,與其餘信息一併發出。在接收請求的頁面,把接收到的信息中的令牌與 Session 中的令牌比較,只有一致的時候才處理請求,處理完成後清理session中的值,不然返回 HTTP 403 拒絕請求或者要求用戶從新登錄驗證身份
令牌來防止 CSRF 有如下幾點要注意:
a.雖然請求令牌原理和驗證碼有類似之處,但不該該像驗證碼同樣,全局使用一個 Session Key。由於請求令牌的方法在理論上是可破解的,破解方式是解析來源頁面的文本,獲取令牌內容。若是全局使用一個 Session Key,那麼危險係數會上升。原則上來講,每一個頁面的請求令牌都應該放在獨立的 Session Key 中。咱們在設計服務器端的時候,能夠稍加封裝,編寫一個令牌工具包,將頁面的標識做爲 Session 中保存令牌的鍵。
b.在 ajax 技術應用較多的場合,由於頗有請求是 JavaScript 發起的,使用靜態的模版輸出令牌值或多或少有些不方便。但不管如何,請不要提供直接獲取令牌值的 API。這麼作無疑是鎖上了大門,卻又把鑰匙放在門口,讓咱們的請求令牌退化爲同步令牌。
c.第一點說了請求令牌理論上是可破解的,因此很是重要的場合,應該考慮使用驗證碼(令牌的一種升級,目前來看破解難度極大),或者要求用戶再次輸入密碼(亞馬遜、淘寶的作法)。但這兩種方式用戶體驗都很差,因此須要產品開發者權衡。
d.不管是普通的請求令牌仍是驗證碼,服務器端驗證過必定記得銷燬。忘記銷燬用過的令牌是個很低級可是殺傷力很大的錯誤。咱們學校的選課系統就有這個問題,驗證碼用完並未銷燬,故只要獲取一次驗證碼圖片,其中的驗證碼能夠在屢次請求中使用(只要再也不次刷新驗證碼圖片),一直用到。
以下也列出一些聽說能有效防範 CSRF,其實效果甚微或甚至無效的作法:
a.經過 referer 斷定來源頁面:referer 是在 HTTP Request Head 裏面的,也就是由請求的發送者決定的。若是我喜歡,能夠給 referer 任何值。固然這個作法並非毫無做用,起碼能夠防小白。但我以爲性價比不如令牌。
b.過濾全部用戶發佈的連接:這個是最無效的作法,由於首先攻擊者不必定要從站內發起請求(上面提到過了),並且就算從站內發起請求,途徑也遠遠不知連接一條。好比 <img src="./create_post.php" /> 就是個不錯的選擇,還不須要用戶去點擊,只要用戶的瀏覽器會自動加載圖片,就會自動發起請求。
c.在請求發起頁面用 alert 彈窗提醒用戶:這個方法看上去能干擾站外經過 iframe 發起的 CSRF,但攻擊者也能夠考慮用 window.alert = function(){}; 把 alert 弄啞,或者乾脆脫離 iframe,使用 Flash 來達到目的。
整體來講,目前防護 CSRF 的諸多方法還沒幾個能完全無解的。 做爲開發者,咱們能作的就是儘可能提升破解難度。當破解難度達到必定程度,網站就逼近於絕對安全的位置了。