跨站腳本攻擊(XSS)已經不是什么新鮮的話題了,甚至很多大公司也為此吃盡苦頭。最簡單直接的防范方法,就是不允許任何html標簽輸入,對用戶輸入進行編碼(htmlencode)。
但是如果想用戶輸入支持一些格式,怎么辦?一種辦法就是很多論壇采用的BB Code的方法。使用特定的標簽代替一些格式。比如:[B]表示粗體,等等。但是,BB Code這種形式并不被廣泛接受,它的表現力實在太差了,而且并不是標準格式。
為了讓用戶的輸入更具表現力,涌現了大量的Html編輯器控件,著名的有FCKEditor,FreeTextBox,Rich TextBox,Cute Editor,TinyMCE等等。比如,博客園的后臺發隨筆就支持Cute Editor和TinyMCE,我個人比較喜歡Cute Editor,功能強大,性能不錯,而且容易定制。
使用這些Html編輯器控件的潛在危險,是用戶可能會輸入一些危險字符,注入到網站中,形成XSS攻擊。一個最簡單的輸入就是:
<javascript>alert('xss')</javascript>
如何防止呢?大致思路有三種:
1. 正則表達式的白名單過濾機制。
2. 正則表達式的黑名單替換機制。
3. 通過DOM對象過濾白名單和黑名單的標簽。
下面這個地址列舉了很多這樣的過濾方法:
http://refactormycode.com/codes/333-sanitize-html
不過,我試了上面鏈接里的一些方法,并不是很好用。有一個更簡單好用的東西,就是AntiXSS,由微軟推出的用于防止XSS攻擊的一個類庫。它的實現原理也是使用白名單機制,不過這個白名單對我們來說是一個黑盒,我用reflector粗略看了一下,也沒找到所謂的白名單在哪里。不過,這個庫確實很好用。
一個MSDN里圖文并茂的使用說明:http://msdn.microsoft.com/en-us/library/aa973813.aspx
其實我用的很簡單,就是AntiXss.GetSafeHtmlFragment(html)方法,這個方法會替換掉html里的危險字符。比如:
var html = "<a href=\"#\" onclick=\"alert();\">aaaaaaaaa</a>javascript<P><IMG SRC=javascript:alert('XSS')><javascript>alert('a')</javascript><IMG src=\"abc.jpg\"><IMG><P>Test</P>";
string safeHtml = AntiXss.GetSafeHtmlFragment(html);
Console.WriteLine(safeHtml);
上面的危險內容被成功替換,返回的內容是:
<a href="">aaaaaaaaa</a>javascript
<p><img src="">alert('a')<img src="abc.jpg"><img></p>
<p>Test</p>
嗯,非常安全。然后,又有一個疑問了,是應該將用戶的輸入過濾之后寫入數據庫呢?還是在輸出界面顯示的時候進行過濾?其實,通常來講,在輸出界面顯示的時候進行過濾就夠了,將用戶輸入過濾后寫入數據庫不是很必要,因為即使這樣也無法保證數據庫中沒有危險的數據。當然,上個雙保險也沒有什么不好的。