HttpRequest的QueryString属性 的一点认识
程序员文章站
2024-03-06 18:05:32
如: 当然我们一般都是按照提示来把framework版本设置2.0来解决。为什么可以这么解决了,还有没有其它的解决方法了。 先让我们看看querystring的源代码吧:...
如:
当然我们一般都是按照提示来把framework版本设置2.0来解决。为什么可以这么解决了,还有没有其它的解决方法了。
先让我们看看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);
this.validatenamevaluecollection(this._querystring, requestvalidationsource.querystring);
}
return this._querystring;
}
}
private void fillinquerystringcollection()
{
byte[] querystringbytes = this.querystringbytes;
if (querystringbytes != null)
{
if (querystringbytes.length != 0)
{
this._querystring.fillfromencodedbytes(querystringbytes, this.querystringencoding);
}
}
else if (!string.isnullorempty(this.querystringtext))
{
this._querystring.fillfromstring(this.querystringtext, true, this.querystringencoding);
}
}
先让我们插入一点 那就是querystring默认已经做了url解码。 其中httpvaluecollection的 fillfromencodedbytes方法如下
internal void fillfromencodedbytes(byte[] bytes, encoding encoding)
{
int num = (bytes != null) ? bytes.length : 0;
for (int i = 0; i < num; i++)
{
string str;
string str2;
this.throwifmaxhttpcollectionkeysexceeded();
int offset = i;
int num4 = -1;
while (i < num)
{
byte num5 = bytes[i];
if (num5 == 0x3d)
{
if (num4 < 0)
{
num4 = i;
}
}
else if (num5 == 0x26)
{
break;
}
i++;
}
if (num4 >= 0)
{
str = httputility.urldecode(bytes, offset, num4 - offset, encoding);
str2 = httputility.urldecode(bytes, num4 + 1, (i - num4) - 1, encoding);
}
else
{
str = null;
str2 = httputility.urldecode(bytes, offset, i - offset, encoding);
}
base.add(str, str2);
if ((i == (num - 1)) && (bytes[i] == 0x26))
{
base.add(null, string.empty);
}
}
}
从这里我们可以看到querystring已经为我们做了解码工作,我们不需要写成 httputility.htmldecode(request.querystring["xxx"])而是直接写成request.querystring["xxx"]就ok了。
现在让我们来看看你querystring的验证,在代码中有
if (this._flags[1])
{
this._flags.clear(1);
this.validatenamevaluecollection(this._querystring, requestvalidationsource.querystring);
}
一看this.validatenamevaluecollection这个方法名称就知道是干什么的了,验证querystring数据;那么在什么情况下验证的了?
让我们看看this._flags[1]在什么地方设置的:
public void validateinput()
{
if (!this._flags[0x8000])
{
this._flags.set(0x8000);
this._flags.set(1);
this._flags.set(2);
this._flags.set(4);
this._flags.set(0x40);
this._flags.set(0x80);
this._flags.set(0x100);
this._flags.set(0x200);
this._flags.set(8);
}
}
而该方法在validateinputifrequiredbyconfig中调用,调用代码
internal void validateinputifrequiredbyconfig()
{
.........
if (httpruntime.requestvalidationmode >= versionutil.framework40)
{
this.validateinput();
}
}
我想现在大家都应该明白为什么错题提示让我们把framework改为2.0了吧。应为在4.0后才验证。这种解决问题的方法是关闭验证,那么我们是否可以改变默认的验证规则了?
让我们看看validatenamevaluecollection
private void validatenamevaluecollection(namevaluecollection nvc, requestvalidationsource requestcollection)
{
int count = nvc.count;
for (int i = 0; i < count; i++)
{
string key = nvc.getkey(i);
if ((key == null) || !key.startswith("__", stringcomparison.ordinal))
{
string str2 = nvc.get(i);
if (!string.isnullorempty(str2))
{
this.validatestring(str2, key, requestcollection);
}
}
}
}
private void validatestring(string value, string collectionkey, requestvalidationsource requestcollection)
{
int num;
value = removenullcharacters(value);
if (!requestvalidator.current.isvalidrequeststring(this.context, value, requestcollection, collectionkey, out num))
{
string str = collectionkey + "=\"";
int startindex = num - 10;
if (startindex <= 0)
{
startindex = 0;
}
else
{
str = str + "...";
}
int length = num + 20;
if (length >= value.length)
{
length = value.length;
str = str + value.substring(startindex, length - startindex) + "\"";
}
else
{
str = str + value.substring(startindex, length - startindex) + "...\"";
}
string requestvalidationsourcename = getrequestvalidationsourcename(requestcollection);
throw new httprequestvalidationexception(sr.getstring("dangerous_input_detected", new object[] { requestvalidationsourcename, str }));
}
}
哦?原来一切都明白了,验证是在requestvalidator做的。
public class requestvalidator
{
// fields
private static requestvalidator _customvalidator;
private static readonly lazy<requestvalidator> _customvalidatorresolver = new lazy<requestvalidator>(new func<requestvalidator>(requestvalidator.getcustomvalidatorfromconfig));
// methods
private static requestvalidator getcustomvalidatorfromconfig()
{
httpruntimesection httpruntime = runtimeconfig.getappconfig().httpruntime;
type userbasetype = configutil.gettype(httpruntime.requestvalidationtype, "requestvalidationtype", httpruntime);
configutil.checkbasetype(typeof(requestvalidator), userbasetype, "requestvalidationtype", httpruntime);
return (requestvalidator) httpruntime.createpublicinstance(userbasetype);
}
internal static void initializeonfirstrequest()
{
requestvalidator local1 = _customvalidatorresolver.value;
}
private static bool isatoz(char c)
{
return (((c >= 'a') && (c <= 'z')) || ((c >= 'a') && (c <= 'z')));
}
protected internal virtual bool isvalidrequeststring(httpcontext context, string value, requestvalidationsource requestvalidationsource, string collectionkey, out int validationfailureindex)
{
if (requestvalidationsource == requestvalidationsource.headers)
{
validationfailureindex = 0;
return true;
}
return !crosssitescriptingvalidation.isdangerousstring(value, out validationfailureindex);
}
// properties
public static requestvalidator current
{
get
{
if (_customvalidator == null)
{
_customvalidator = _customvalidatorresolver.value;
}
return _customvalidator;
}
set
{
if (value == null)
{
throw new argumentnullexception("value");
}
_customvalidator = value;
}
}
}
主要的验证方法还是在crosssitescriptingvalidation.isdangerousstring(value, out validationfailureindex);而crosssitescriptingvalidation是一个内部类,无法修改。
让我们看看crosssitescriptingvalidation类大代码把
internal static class crosssitescriptingvalidation
{
// fields
private static char[] startingchars = new char[] { '<', '&' };
// methods
private static bool isatoz(char c)
{
return (((c >= 'a') && (c <= 'z')) || ((c >= 'a') && (c <= 'z')));
}
internal static bool isdangerousstring(string s, out int matchindex)
{
matchindex = 0;
int startindex = 0;
while (true)
{
int num2 = s.indexofany(startingchars, startindex);
if (num2 < 0)
{
return false;
}
if (num2 == (s.length - 1))
{
return false;
}
matchindex = num2;
char ch = s[num2];
if (ch != '&')
{
if ((ch == '<') && ((isatoz(s[num2 + 1]) || (s[num2 + 1] == '!')) || ((s[num2 + 1] == '/') || (s[num2 + 1] == '?'))))
{
return true;
}
}
else if (s[num2 + 1] == '#')
{
return true;
}
startindex = num2 + 1;
}
}
internal static bool isdangerousurl(string s)
{
if (string.isnullorempty(s))
{
return false;
}
s = s.trim();
int length = s.length;
if (((((length > 4) && ((s[0] == 'h') || (s[0] == 'h'))) && ((s[1] == 't') || (s[1] == 't'))) && (((s[2] == 't') || (s[2] == 't')) && ((s[3] == 'p') || (s[3] == 'p')))) && ((s[4] == ':') || (((length > 5) && ((s[4] == 's') || (s[4] == 's'))) && (s[5] == ':'))))
{
return false;
}
if (s.indexof(':') == -1)
{
return false;
}
return true;
}
internal static bool isvalidjavascriptid(string id)
{
if (!string.isnullorempty(id))
{
return codegenerator.isvalidlanguageindependentidentifier(id);
}
return true;
}
}
结果我们发现 <! </ <? <[a-za-z] 这些情况验证都是通不过的。
所以我们只需要重写requestvalidator就可以了。
例如我们现在需要处理我们现在需要过滤querystring中k=&...的情况
public class custrequestvalidator : requestvalidator
{
protected override bool isvalidrequeststring(httpcontext context, string value, requestvalidationsource requestvalidationsource, string collectionkey, out int validationfailureindex)
{
validationfailureindex = 0;
//我们现在需要过滤querystring中k=&...的情况
if (requestvalidationsource == requestvalidationsource.querystring&&collectionkey.equals("k")&& value.startswith("&"))
{
return true;
}
return base.isvalidrequeststring(context, value, requestvalidationsource, collectionkey, out validationfailureindex);
}
}
<httpruntime requestvalidationtype="mvcapp.custrequestvalidator"/>
个人在这里只是提供一个思想,欢迎大家拍砖!
当然我们一般都是按照提示来把framework版本设置2.0来解决。为什么可以这么解决了,还有没有其它的解决方法了。
先让我们看看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);
this.validatenamevaluecollection(this._querystring, requestvalidationsource.querystring);
}
return this._querystring;
}
}
private void fillinquerystringcollection()
{
byte[] querystringbytes = this.querystringbytes;
if (querystringbytes != null)
{
if (querystringbytes.length != 0)
{
this._querystring.fillfromencodedbytes(querystringbytes, this.querystringencoding);
}
}
else if (!string.isnullorempty(this.querystringtext))
{
this._querystring.fillfromstring(this.querystringtext, true, this.querystringencoding);
}
}
先让我们插入一点 那就是querystring默认已经做了url解码。 其中httpvaluecollection的 fillfromencodedbytes方法如下
复制代码 代码如下:
internal void fillfromencodedbytes(byte[] bytes, encoding encoding)
{
int num = (bytes != null) ? bytes.length : 0;
for (int i = 0; i < num; i++)
{
string str;
string str2;
this.throwifmaxhttpcollectionkeysexceeded();
int offset = i;
int num4 = -1;
while (i < num)
{
byte num5 = bytes[i];
if (num5 == 0x3d)
{
if (num4 < 0)
{
num4 = i;
}
}
else if (num5 == 0x26)
{
break;
}
i++;
}
if (num4 >= 0)
{
str = httputility.urldecode(bytes, offset, num4 - offset, encoding);
str2 = httputility.urldecode(bytes, num4 + 1, (i - num4) - 1, encoding);
}
else
{
str = null;
str2 = httputility.urldecode(bytes, offset, i - offset, encoding);
}
base.add(str, str2);
if ((i == (num - 1)) && (bytes[i] == 0x26))
{
base.add(null, string.empty);
}
}
}
从这里我们可以看到querystring已经为我们做了解码工作,我们不需要写成 httputility.htmldecode(request.querystring["xxx"])而是直接写成request.querystring["xxx"]就ok了。
现在让我们来看看你querystring的验证,在代码中有
复制代码 代码如下:
if (this._flags[1])
{
this._flags.clear(1);
this.validatenamevaluecollection(this._querystring, requestvalidationsource.querystring);
}
一看this.validatenamevaluecollection这个方法名称就知道是干什么的了,验证querystring数据;那么在什么情况下验证的了?
让我们看看this._flags[1]在什么地方设置的:
复制代码 代码如下:
public void validateinput()
{
if (!this._flags[0x8000])
{
this._flags.set(0x8000);
this._flags.set(1);
this._flags.set(2);
this._flags.set(4);
this._flags.set(0x40);
this._flags.set(0x80);
this._flags.set(0x100);
this._flags.set(0x200);
this._flags.set(8);
}
}
而该方法在validateinputifrequiredbyconfig中调用,调用代码
复制代码 代码如下:
internal void validateinputifrequiredbyconfig()
{
.........
if (httpruntime.requestvalidationmode >= versionutil.framework40)
{
this.validateinput();
}
}
我想现在大家都应该明白为什么错题提示让我们把framework改为2.0了吧。应为在4.0后才验证。这种解决问题的方法是关闭验证,那么我们是否可以改变默认的验证规则了?
让我们看看validatenamevaluecollection
复制代码 代码如下:
private void validatenamevaluecollection(namevaluecollection nvc, requestvalidationsource requestcollection)
{
int count = nvc.count;
for (int i = 0; i < count; i++)
{
string key = nvc.getkey(i);
if ((key == null) || !key.startswith("__", stringcomparison.ordinal))
{
string str2 = nvc.get(i);
if (!string.isnullorempty(str2))
{
this.validatestring(str2, key, requestcollection);
}
}
}
}
private void validatestring(string value, string collectionkey, requestvalidationsource requestcollection)
{
int num;
value = removenullcharacters(value);
if (!requestvalidator.current.isvalidrequeststring(this.context, value, requestcollection, collectionkey, out num))
{
string str = collectionkey + "=\"";
int startindex = num - 10;
if (startindex <= 0)
{
startindex = 0;
}
else
{
str = str + "...";
}
int length = num + 20;
if (length >= value.length)
{
length = value.length;
str = str + value.substring(startindex, length - startindex) + "\"";
}
else
{
str = str + value.substring(startindex, length - startindex) + "...\"";
}
string requestvalidationsourcename = getrequestvalidationsourcename(requestcollection);
throw new httprequestvalidationexception(sr.getstring("dangerous_input_detected", new object[] { requestvalidationsourcename, str }));
}
}
哦?原来一切都明白了,验证是在requestvalidator做的。
复制代码 代码如下:
public class requestvalidator
{
// fields
private static requestvalidator _customvalidator;
private static readonly lazy<requestvalidator> _customvalidatorresolver = new lazy<requestvalidator>(new func<requestvalidator>(requestvalidator.getcustomvalidatorfromconfig));
// methods
private static requestvalidator getcustomvalidatorfromconfig()
{
httpruntimesection httpruntime = runtimeconfig.getappconfig().httpruntime;
type userbasetype = configutil.gettype(httpruntime.requestvalidationtype, "requestvalidationtype", httpruntime);
configutil.checkbasetype(typeof(requestvalidator), userbasetype, "requestvalidationtype", httpruntime);
return (requestvalidator) httpruntime.createpublicinstance(userbasetype);
}
internal static void initializeonfirstrequest()
{
requestvalidator local1 = _customvalidatorresolver.value;
}
private static bool isatoz(char c)
{
return (((c >= 'a') && (c <= 'z')) || ((c >= 'a') && (c <= 'z')));
}
protected internal virtual bool isvalidrequeststring(httpcontext context, string value, requestvalidationsource requestvalidationsource, string collectionkey, out int validationfailureindex)
{
if (requestvalidationsource == requestvalidationsource.headers)
{
validationfailureindex = 0;
return true;
}
return !crosssitescriptingvalidation.isdangerousstring(value, out validationfailureindex);
}
// properties
public static requestvalidator current
{
get
{
if (_customvalidator == null)
{
_customvalidator = _customvalidatorresolver.value;
}
return _customvalidator;
}
set
{
if (value == null)
{
throw new argumentnullexception("value");
}
_customvalidator = value;
}
}
}
主要的验证方法还是在crosssitescriptingvalidation.isdangerousstring(value, out validationfailureindex);而crosssitescriptingvalidation是一个内部类,无法修改。
让我们看看crosssitescriptingvalidation类大代码把
复制代码 代码如下:
internal static class crosssitescriptingvalidation
{
// fields
private static char[] startingchars = new char[] { '<', '&' };
// methods
private static bool isatoz(char c)
{
return (((c >= 'a') && (c <= 'z')) || ((c >= 'a') && (c <= 'z')));
}
internal static bool isdangerousstring(string s, out int matchindex)
{
matchindex = 0;
int startindex = 0;
while (true)
{
int num2 = s.indexofany(startingchars, startindex);
if (num2 < 0)
{
return false;
}
if (num2 == (s.length - 1))
{
return false;
}
matchindex = num2;
char ch = s[num2];
if (ch != '&')
{
if ((ch == '<') && ((isatoz(s[num2 + 1]) || (s[num2 + 1] == '!')) || ((s[num2 + 1] == '/') || (s[num2 + 1] == '?'))))
{
return true;
}
}
else if (s[num2 + 1] == '#')
{
return true;
}
startindex = num2 + 1;
}
}
internal static bool isdangerousurl(string s)
{
if (string.isnullorempty(s))
{
return false;
}
s = s.trim();
int length = s.length;
if (((((length > 4) && ((s[0] == 'h') || (s[0] == 'h'))) && ((s[1] == 't') || (s[1] == 't'))) && (((s[2] == 't') || (s[2] == 't')) && ((s[3] == 'p') || (s[3] == 'p')))) && ((s[4] == ':') || (((length > 5) && ((s[4] == 's') || (s[4] == 's'))) && (s[5] == ':'))))
{
return false;
}
if (s.indexof(':') == -1)
{
return false;
}
return true;
}
internal static bool isvalidjavascriptid(string id)
{
if (!string.isnullorempty(id))
{
return codegenerator.isvalidlanguageindependentidentifier(id);
}
return true;
}
}
结果我们发现 <! </ <? <[a-za-z] 这些情况验证都是通不过的。
所以我们只需要重写requestvalidator就可以了。
例如我们现在需要处理我们现在需要过滤querystring中k=&...的情况
复制代码 代码如下:
public class custrequestvalidator : requestvalidator
{
protected override bool isvalidrequeststring(httpcontext context, string value, requestvalidationsource requestvalidationsource, string collectionkey, out int validationfailureindex)
{
validationfailureindex = 0;
//我们现在需要过滤querystring中k=&...的情况
if (requestvalidationsource == requestvalidationsource.querystring&&collectionkey.equals("k")&& value.startswith("&"))
{
return true;
}
return base.isvalidrequeststring(context, value, requestvalidationsource, collectionkey, out validationfailureindex);
}
}
<httpruntime requestvalidationtype="mvcapp.custrequestvalidator"/>
个人在这里只是提供一个思想,欢迎大家拍砖!
推荐阅读
-
HttpRequest的QueryString属性 的一点认识
-
HttpRequest的QueryString属性 的一点认识
-
【探讨】关于标记语言 ——对JSTL语言的一点点认识 出版XML网络应用数据结构编程
-
对snat, dnat, 回环的一点认识
-
对javascript的一点点认识总结《javascript高级程序设计》读书笔记_javascript技巧
-
OutputCacheProvider OutputCache的一点点认识
-
对于Thread的start()方法和run()方法区别的一点认识
-
php magic_quotes_gpc的一点认识与分析
-
ASP.NET Web API queryString访问的一点总结
-
Asp中Server.ScriptTimeOut属性需要注意的一点分析