好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

让encodeURIComponent过的尖括号躲过ASP.NET的误拦截

让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 %3 C  T %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的问题。因为在写博客过程中,你要清楚地表达出来,来不得半点马虎,你会对问题进行更深入的研究。

一个东西,你想明白了,并不代表你真正理解。只有你清楚地表达出来,让别人能明白,才说明你真正理解。

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于让encodeURIComponent过的尖括号躲过ASP.NET的误拦截的详细内容...

  阅读:47次

上一篇: 千万级数据的分页

下一篇:模式原则