躲不過也要躲:讓encodeURIComponent過的尖括號躲過ASP.NET的誤攔截


問題所處環境:IIS 7.5, ASP.NET 4.0, 應用程序池(Application Pool)運行於集成模式(Integrated)。

今天一位園友向我們反饋用網摘收藏博客文章LINQ那些事(9)-解析Table<T>.Attach引發的異常和解決方法時出錯(注意:文章標題中有尖括號)。

我們查了一下,具體的錯誤信息是:

A potentially dangerous Request.QueryString value was detected from the client (t="...9)-解析Table<T>.Attach引發的異常和解決方法...").

錯誤信息分析:為了防止XSS跨站腳本攻擊,IIS的默認安全設置不允許查詢字符串中包含尖括號,而這次網摘收藏操作卻違反了這個規定,於是引發了這個錯誤。

對於這個問題,你也許會說這么簡單的問題也好意思寫篇博客,肯定是通過url參數傳遞標題時沒有編碼。如果真是這樣,也會寫篇博客,但不是技術分享,而是檢討書。

剛開始看到這個錯誤時,的確閃過這樣的念頭 —— 難道真的忘了編碼?。。。不會的,記得編了。打開代碼一看,松了一口氣,檢討書不用寫了,但技術分享必須的,當然前提是解決了問題。

上網摘中用到的js代碼:

var url = 'http://home.cnblogs.com/wz/create?t=' + encodeURIComponent(document.title);

看!剛剛的!encodeURIComponent,經過無數次實踐證明過的有效的Javascript Url Encode方式。

可是,現在竟然出問題了。。。

首先懷疑是不是頁面標題中沒有對標題內容進行HTML編碼。檢查確認,編碼了:

<title>LINQ那些事(9)-解析Table&lt;T&gt;.Attach引發的異常和解決方法 - 海南K.K - 博客園</title>

接着,看一下document.title的值:

LINQ那些事(9)-解析Table<T>.Attach引發的異常和解決方法  - 海南K.K - 博客園

竟然自動進行HTML解碼了!這還是第一次發現!解碼也沒關系啊,encodeURIComponent對尖括號也會編碼。

繼續前進,看一下encodeURIComponent(document.title)的值:

LINQ%E9%82%A3%E4%BA%9B%E4%BA%8B(9)-%E8%A7%A3%E6%9E%90Table%3CT%3E.Attach%E5%BC%95%E5%8F%91%E7%9A%84%E5%BC%82%E5%B8%B8%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95%20-%20%E6%B5%B7%E5%8D%97K.K%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD

<被編碼為%3C,>被編碼為%3E,正常啊。明明是編了碼的尖括號,IIS怎么會報錯呢?難道瀏覽器偷偷解了碼,再發送給IIS。

特此檢查了一下瀏覽器請求的URL,瀏覽器對編了碼的URL絲毫未動。

難道是IIS惹的禍?看一下IIS日志中記錄的URL:

日志記錄說明了IIS收到的也是編過碼的尖括號。難道罪魁禍首是IIS!

也就是說IIS在判斷時,先將查詢字符串進行解碼,將%3C解碼為<,將%3E解碼>,然后發現有尖括號,然后出錯!

是不是這樣呢?不入虎穴,焉得真相。根據錯誤信息,用ILSPY查看HttpRequest的源代碼。

錯誤信息如下:

用ILSPY一路追蹤:

1. HttpRequest.QueryString

public NameValueCollection QueryString
{
get
{
if (this._queryString == null)
{
this._queryString = new HttpValueCollection();
if (this._wr != null)
{
this.FillInQueryStringCollection();
}
this._queryString.MakeReadOnly();
}
if (this._flags[1])
{
this._flags.Clear(1);
HttpRequest.ValidateNameValueCollection(this._queryString, "Request.QueryString");
}
return this._queryString;
}
}

2. FillInQueryStringCollection()

// System.Web.HttpRequest
private void FillInQueryStringCollection()
{
byte[] queryStringBytes = this.QueryStringBytes;
if (queryStringBytes != null)
{
if (queryStringBytes.Length != 0)
{
this._queryString.FillFromEncodedBytes(queryStringBytes, this.QueryStringEncoding);
return;
}
}
else
{
if (!string.IsNullOrEmpty(this.QueryStringText))
{
this._queryString.FillFromString(this.QueryStringText, true, this.QueryStringEncoding);
}
}
}

3. FillFromEncodedBytes

// System.Web.HttpValueCollection
internal void FillFromEncodedBytes(byte[] bytes, Encoding encoding)
{
int num = (bytes != null) ? bytes.Length : 0;
for (int i = 0; i < num; i++)
{
this.ThrowIfMaxHttpCollectionKeysExceeded();
int num2 = i;
int num3 = -1;
while (i < num)
{
byte b = bytes[i];
if (b == 61)
{
if (num3 < 0)
{
num3 = i;
}
}
else
{
if (b == 38)
{
break;
}
}
i++;
}
string name;
string value;
if (num3 >= 0)
{
name = HttpUtility.UrlDecode(bytes, num2, num3 - num2, encoding);
value = HttpUtility.UrlDecode(bytes, num3 + 1, i - num3 - 1, encoding);
}
else
{
name = null;
value = HttpUtility.UrlDecode(bytes, num2, i - num2, encoding);
}
base.Add(name, value);
if (i == num - 1 && bytes[i] == 38)
{
base.Add(null, string.Empty);
}
}
}

就是在這里進行解碼的,調用的就是HttpUtility.UrlDecode。 

原來罪魁禍不是IIS,是ASP.NET!

問題原因小結: 

ASP.NET在檢測XSS跨站腳本攻擊時,會將查詢字符串解碼,然后調用System.Web.CrossSiteScriptingValidation.IsDangerousString()進行檢查。所以任何對查詢字符串中的尖括號進行直接的UrlEncode編碼操作(比如Javascript的encodeURIComponent, escape, encodeURI)都無法逃過ASP.NET的檢查。

那沒有解決方法呢?有!我們找到了,並且已經在實際中使用,不信的話,可以用網摘收藏一下LINQ那些事(9)-解析Table<T>.Attach引發的異常和解決方法

解決方法:

1. 通過下面的代碼獲取原裝的未進行過HTML解碼的頁面標題(Javascript代碼),這里 < 變成了 &lt; , > 變成了 &gt;:

var title = document.getElementsByTagName('title')[0].innerHTML;

(注意:前面已經提過,document.title會對<title></title>中的內容自動進行HTML解碼,所以不要用它。)

2. 然后通過Javscript的encodeURIComponent進一步編碼,這樣可以躲過ASP.NET的XSS跨站腳本攻擊檢查(檢查時,ASP.NET得到的是 &lt; 與 &gt; )。

3. 在ASP.NET程序中獲取這個查詢字符串時,需要進行額外的HtmlDecode操作,C#代碼如下:

HttpUtility.HtmlDecode(Request.QueryString["t"]);

感言

解決一個問題,最好的慶祝方式就是寫一篇博客!不僅可以分享給別人,自己還會有額外的收獲!而且一定會有!

本來寫這篇博客時,我用的標題是“[Javascript]document.title 引起的Html Decode 問題”,當時以為是Javascript的問題,寫博客過程中才發現是ASP.NET的問題。因為在寫博客過程中,你要清楚地表達出來,來不得半點馬虎,你會對問題進行更深入的研究。

一個東西,你想明白了,並不代表你真正理解。只有你清楚地表達出來,讓別人能明白,才說明你真正理解。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM