从客户端检测到潜在危险的 Request.Form 值
-
09-06-2019 - |
题
每当用户发布包含以下内容的内容时 <
或者 >
在我的网络应用程序的页面中,我抛出了这个异常。
我不想讨论因为有人在文本框中输入了一个字符而抛出异常或使整个 Web 应用程序崩溃的智能性,但我正在寻找一种优雅的方法来处理这个问题。
捕获异常并显示
发生错误,请返回并重新输入整个表单,但这次请不要使用 <
对我来说似乎不够专业。
禁用后验证(validateRequest="false"
)肯定会避免此错误,但它会使页面容易受到多种攻击。
理想情况下:当发生包含 HTML 限制字符的回发时,Form 集合中的回发值将自动进行 HTML 编码。所以 .Text
我的文本框的属性将是 something & lt; html & gt;
有什么办法可以从处理程序中做到这一点吗?
解决方案
我认为您试图对所有发布的数据进行编码,从而从错误的角度攻击它。
请注意,一个“<
“也可以来自其他外部来源,例如数据库字段、配置、文件、提要等。
此外, ”<
“本质上并不危险。它仅在特定情况下才危险:当编写尚未编码为 HTML 输出的字符串时(由于 XSS)。
在其他上下文中,不同的子字符串是危险的,例如,如果您将用户提供的 URL 写入链接,则子字符串“javascript:
“可能有危险。另一方面,在 SQL 查询中插入字符串时,单引号字符很危险,但如果它是从表单提交的名称的一部分或从数据库字段读取的名称,则完全安全。
底线是:您无法过滤随机输入中的危险字符,因为任何字符在适当的情况下都可能是危险的。您应该在某些特定字符可能变得危险的位置进行编码,因为它们会进入具有特殊含义的不同子语言。将字符串写入 HTML 时,应使用 Server.HtmlEncode 对 HTML 中具有特殊含义的字符进行编码。如果将字符串传递给动态 SQL 语句,则应该对不同的字符进行编码(或者更好的是,让框架通过使用准备好的语句等为您完成此操作)。
什么时候 您确定在将字符串传递给 HTML 的所有地方都进行了 HTML 编码,然后设置 validateRequest="false"
在里面 <%@ Page ... %>
指令在你的 .aspx
文件。
在 .NET 4 中,您可能需要做更多的事情。有时还需要添加 <httpRuntime requestValidationMode="2.0" />
到 web.config (参考).
其他提示
如果您使用 ASP.NET MVC,则可以使用不同的解决方案来解决此错误:
- ASP.NET MVC – 页面 validateRequest=false 不起作用?
- 为什么 ValidateInput(False) 不起作用?
- ASP.NET MVC RC1、VALIDATEINPUT、潜在危险请求和陷阱
C# 示例:
[HttpPost, ValidateInput(false)]
public ActionResult Edit(FormCollection collection)
{
// ...
}
视觉基本样本:
<AcceptVerbs(HttpVerbs.Post), ValidateInput(False)> _
Function Edit(ByVal collection As FormCollection) As ActionResult
...
End Function
在 ASP.NET MVC(从版本 3 开始)中,您可以添加 AllowHtml
属性到模型上的属性。
它允许请求在模型绑定期间通过跳过属性的请求验证来包含 HTML 标记。
[AllowHtml]
public string Description { get; set; }
如果您使用的是 .NET 4.0,请确保将其添加到您的 网络配置 文件内的 <system.web>
标签:
<httpRuntime requestValidationMode="2.0" />
在.NET 2.0中,请求验证仅适用于 aspx
要求。在 .NET 4.0 中,这已扩展为包括 全部 要求。您可以恢复到 仅有的 处理时执行XSS验证 .aspx
通过指定:
requestValidationMode="2.0"
您可以禁用请求验证 完全 通过指定:
validateRequest="false"
对于 ASP.NET 4.0,您可以允许将标记作为特定页面而不是整个站点的输入,方法是将其全部放入 <location>
元素。这将确保您的所有其他页面都是安全的。你不需要把 ValidateRequest="false"
在您的 .aspx 页面中。
<configuration>
...
<location path="MyFolder/.aspx">
<system.web>
<pages validateRequest="false" />
<httpRuntime requestValidationMode="2.0" />
</system.web>
</location>
...
</configuration>
在 web.config 中控制这一点更安全,因为您可以在站点级别查看哪些页面允许标记作为输入。
您仍然需要以编程方式验证禁用请求验证的页面上的输入。
前面的答案很好,但没有人说如何排除单个字段进行 HTML/JavaScript 注入验证。我不知道以前的版本,但在 MVC3 Beta 中你可以这样做:
[HttpPost, ValidateInput(true, Exclude = "YourFieldName")]
public virtual ActionResult Edit(int id, FormCollection collection)
{
...
}
这仍然会验证除排除字段之外的所有字段。这样做的好处是,您的验证属性仍然会验证该字段,但您只是不会收到“从客户端检测到潜在危险的 Request.Form 值”异常。
我用它来验证正则表达式。我制作了自己的 ValidationAttribute 来查看正则表达式是否有效。由于正则表达式可以包含看起来像脚本的内容,因此我应用了上面的代码 - 仍在检查正则表达式是否有效,但如果它包含脚本或 HTML,则不会检查。
在 ASP.NET MVC 中,您需要在 web.config 中设置 requestValidationMode="2.0" 和 validateRequest="false",并将 ValidateInput 属性应用于控制器操作:
<httpRuntime requestValidationMode="2.0"/>
<configuration>
<system.web>
<pages validateRequest="false" />
</system.web>
</configuration>
和
[Post, ValidateInput(false)]
public ActionResult Edit(string message) {
...
}
你可以 HTML 编码 文本框内容,但不幸的是,这并不能阻止异常的发生。根据我的经验,没有办法解决,您必须禁用页面验证。通过这样做,你是在说:“我会小心的,我保证。”
对于 MVC,通过添加忽略输入验证
[验证输入(假)]
在控制器中的每个操作上方。
您可以在 Global.asax 中捕获该错误。我仍然想验证,但显示适当的消息。在下面列出的博客上,提供了这样的示例。
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
if (ex is HttpRequestValidationException)
{
Response.Clear();
Response.StatusCode = 200;
Response.Write(@"[html]");
Response.End();
}
}
重定向到另一个页面似乎也是对异常的合理响应。
http://www.romsteady.net/blog/2007/06/how-to-catch-httprequestvalidationexcep.html
这个问题的答案很简单:
var varname = Request.Unvalidated["parameter_name"];
这将禁用对特定请求的验证。
请记住,某些 .NET 控件会自动对输出进行 HTML 编码。例如,在 TextBox 控件上设置 .Text 属性将自动对其进行编码。这具体意味着转换 <
进入 <
, >
进入 >
和 &
进入 &
. 。所以做这件事要小心...
myTextBox.Text = Server.HtmlEncode(myStringFromDatabase); // Pseudo code
然而,HyperLink、Literal 和 Label 的 .Text 属性不会对内容进行 HTML 编码,因此包装 Server.HtmlEncode();如果您想防止在这些属性上设置的任何内容,则必须这样做 <script> window.location = "http://www.google.com"; </script>
防止输出到您的页面并随后执行。
做一些实验看看什么被编码,什么没有被编码。
在 web.config 文件的标记内,插入具有 requestValidationMode="2.0" 属性的 httpRuntime 元素。还要在页面元素中添加 validateRequest="false" 属性。
例子:
<configuration>
<system.web>
<httpRuntime requestValidationMode="2.0" />
</system.web>
<pages validateRequest="false">
</pages>
</configuration>
如果您不想禁用 ValidateRequest,则需要实现 JavaScript 函数以避免异常。这不是最好的选择,但它确实有效。
function AlphanumericValidation(evt)
{
var charCode = (evt.charCode) ? evt.charCode : ((evt.keyCode) ? evt.keyCode :
((evt.which) ? evt.which : 0));
// User type Enter key
if (charCode == 13)
{
// Do something, set controls focus or do anything
return false;
}
// User can not type non alphanumeric characters
if ( (charCode < 48) ||
(charCode > 122) ||
((charCode > 57) && (charCode < 65)) ||
((charCode > 90) && (charCode < 97))
)
{
// Show a message or do something
return false;
}
}
然后在代码后面的 PageLoad 事件中,使用以下代码将该属性添加到控件:
Me.TextBox1.Attributes.Add("OnKeyPress", "return AlphanumericValidation(event);")
似乎还没有人提到下面的内容,但它解决了我的问题。在有人说是的之前,它是 Visual Basic...恶心。
<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Example.aspx.vb" Inherits="Example.Example" **ValidateRequest="false"** %>
我不知道是否有任何缺点,但对我来说这非常有效。
另一个解决方案是:
protected void Application_Start()
{
...
RequestValidator.Current = new MyRequestValidator();
}
public class MyRequestValidator: RequestValidator
{
protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
{
bool result = base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
if (!result)
{
// Write your validation here
if (requestValidationSource == RequestValidationSource.Form ||
requestValidationSource == RequestValidationSource.QueryString)
return true; // Suppress error message
}
return result;
}
}
如果您使用的是框架 4.0,则 web.config 中的条目 (<pages validateRequest="false" />)
<configuration>
<system.web>
<pages validateRequest="false" />
</system.web>
</configuration>
如果您使用的是框架 4.5,则 web.config 中的条目 (requestValidationMode="2.0")
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" requestValidationMode="2.0"/>
</system.web>
如果您只想要单页,那么在您的 aspx 文件中,您应该将第一行如下所示:
<%@ Page EnableEventValidation="false" %>
如果您已经有类似 <%@ Page 的内容,那么只需添加其余的 => EnableEventValidation="false"
%>
我建议不要这样做。
在 ASP.NET 中,您可以捕获异常并对其执行某些操作,例如显示友好消息或重定向到另一个页面...您也可以自己处理验证......
显示友好消息:
protected override void OnError(EventArgs e)
{
base.OnError(e);
var ex = Server.GetLastError().GetBaseException();
if (ex is System.Web.HttpRequestValidationException)
{
Response.Clear();
Response.Write("Invalid characters."); // Response.Write(HttpUtility.HtmlEncode(ex.Message));
Response.StatusCode = 200;
Response.End();
}
}
我想你可以在一个模块中完成它;但这留下了一些悬而未决的问题;如果你想将输入保存到数据库怎么办?突然之间,因为您将编码数据保存到数据库中,您最终信任来自数据库的输入,这可能是一个坏主意。理想情况下,您将原始未编码数据存储在数据库中并每次进行编码。
禁用每页级别的保护,然后每次都进行编码是更好的选择。
您应该查看更新、更完整的而不是使用 Server.HtmlEncode 防XSS库 来自微软 ACE 团队。
我找到了一个使用 JavaScript 对数据进行编码的解决方案,该数据在 .NET 中进行解码(并且不需要 jQuery)。
- 使文本框成为 HTML 元素(如 textarea)而不是 ASP 元素。
- 添加隐藏字段。
将以下 JavaScript 函数添加到标头中。
function boo(){targetText = document.getElementById(“ shidendfield1”);sourceText = document.getElementById("用户框");targetText.value = escape(sourceText.innerText);}
在您的文本区域中,包含一个调用 boo() 的 onchange:
<textarea id="userbox" onchange="boo();"></textarea>
最后,在.NET中,使用
string val = Server.UrlDecode(HiddenField1.Value);
我知道这是单向的 - 如果您需要双向,则必须发挥创意,但是如果您无法编辑 web.config,这提供了一种解决方案
这是我(MC9000)通过 jQuery 提出并使用的一个示例:
$(document).ready(function () {
$("#txtHTML").change(function () {
var currentText = $("#txtHTML").text();
currentText = escape(currentText); // Escapes the HTML including quotations, etc
$("#hidHTML").val(currentText); // Set the hidden field
});
// Intercept the postback
$("#btnMyPostbackButton").click(function () {
$("#txtHTML").val(""); // Clear the textarea before POSTing
// If you don't clear it, it will give you
// the error due to the HTML in the textarea.
return true; // Post back
});
});
和标记:
<asp:HiddenField ID="hidHTML" runat="server" />
<textarea id="txtHTML"></textarea>
<asp:Button ID="btnMyPostbackButton" runat="server" Text="Post Form" />
这很好用。如果黑客试图通过绕过 JavaScript 进行发布,他们只会看到错误。您也可以将所有这些数据编码保存在数据库中,然后取消转义(在服务器端),并在显示在其他地方之前解析和检查攻击。
原因
默认情况下,ASP.NET 会验证所有输入控件是否存在可能导致以下问题的潜在不安全内容: 跨站脚本 (XSS) 和 SQL注入. 。因此,它通过抛出上述异常来禁止此类内容。默认情况下,建议允许在每次回发时进行此检查。
解决方案
在许多情况下,您需要通过富文本框或富文本编辑器将 HTML 内容提交到页面。在这种情况下,您可以通过在 @Page
指令为 false。
<%@ Page Language="C#" AutoEventWireup="true" ValidateRequest = "false" %>
这将禁用对已将 ValidateRequest 标志设置为 false 的页面的请求验证。如果您想禁用此功能,请检查整个 Web 应用程序;您需要在 web.config <system.web> 部分中将其设置为 false
<pages validateRequest ="false" />
对于 .NET 4.0 或更高版本的框架,您还需要在 <system.web> 部分添加以下行才能使上述工作正常进行。
<httpRuntime requestValidationMode = "2.0" />
就是这样。我希望这可以帮助您摆脱上述问题。
这里的其他解决方案很好,但是必须将 [AllowHtml] 应用于每个模型属性,这有点麻烦,特别是如果您在一个规模不错的网站上有超过 100 个模型。
如果像我一样,您想将这个(恕我直言,毫无意义)功能关闭站点范围,您可以覆盖基本控制器中的 Execute() 方法(如果您还没有基本控制器,我建议您制作一个,它们可以是对于应用常见功能非常有用)。
protected override void Execute(RequestContext requestContext)
{
// Disable requestion validation (security) across the whole site
ValidateRequest = false;
base.Execute(requestContext);
}
只需确保对来自用户输入的视图中的所有内容进行 HTML 编码(无论如何,这是带有 Razor 的 ASP.NET MVC 3 中的默认行为,因此除非出于某种奇怪的原因,您正在使用 Html.Raw() )不应该需要这个功能。
我也遇到了这个错误。
就我而言,用户输入了重音字符 á
在角色名称中(关于 ASP.NET 成员资格提供程序)。
我将角色名称传递给一个方法来向用户授予该角色,并且 $.ajax
发布请求惨遭失败...
我这样做是为了解决问题:
代替
data: { roleName: '@Model.RoleName', users: users }
做这个
data: { roleName: '@Html.Raw(@Model.RoleName)', users: users }
@Html.Raw
成功了。
我将角色名称作为 HTML 值获取 roleName="Cadastro bás"
. 。这个值与 HTML 实体 á
被 ASP.NET MVC 阻止。现在我得到了 roleName
参数值应该是这样的: roleName="Cadastro Básico"
并且 ASP.NET MVC 引擎不会再阻止该请求。
如果您确实需要特殊字符,例如,请禁用页面验证, >
, , <
, , ETC。然后确保当显示用户输入时,数据是 HTML 编码的。
页面验证存在安全漏洞,因此可以绕过。此外,不应仅依赖页面验证。
您还可以使用 JavaScript 转义(字符串) 替换特殊字符的函数。然后服务器端使用Server。URL解码(字符串) 将其切换回来。
这样您就不必关闭输入验证,并且其他程序员会更清楚该字符串可能包含 HTML 内容。
我最终在每次回发之前使用 JavaScript 来检查您不需要的字符,例如:
<asp:Button runat="server" ID="saveButton" Text="Save" CssClass="saveButton" OnClientClick="return checkFields()" />
function checkFields() {
var tbs = new Array();
tbs = document.getElementsByTagName("input");
var isValid = true;
for (i=0; i<tbs.length; i++) {
if (tbs(i).type == 'text') {
if (tbs(i).value.indexOf('<') != -1 || tbs(i).value.indexOf('>') != -1) {
alert('<> symbols not allowed.');
isValid = false;
}
}
}
return isValid;
}
虽然我的页面主要是数据输入,并且很少有元素进行回发,但至少保留了它们的数据。
只要有这些 仅有的 “<”和“>”(而不是双引号本身)字符,并且您在 <input value=" 等上下文中使用它们这" />,你很安全(而对于 <textarea>这个</textarea> 当然,你会很容易受到攻击)。这可能会简化你的情况,但是对于 任何事物 更多使用其他发布的解决方案之一。
如果您只是想告诉用户不要使用 < 和 >,但是,您不希望事先处理/回发整个表单(并丢失所有输入),您是否可以简单地输入字段周围的验证器来筛选那些(可能还有其他潜在危险)字符?
这些建议都不适合我。无论如何,我不想关闭整个网站的此功能,因为 99% 的时间我不希望我的用户将 HTML 放置在 Web 表单上。我刚刚创建了自己的解决方法,因为我是唯一使用此特定应用程序的人。我在后面的代码中将输入转换为 HTML 并将其插入到我的数据库中。
你可以使用类似的东西:
var nvc = Request.Unvalidated().Form;
之后, nvc["yourKey"]
应该管用。