如何在asp.net中允许在文本框中提交HTML标签?

27

首先,我想让大家知道,我正在使用的是一个aspx引擎而不是Razor引擎。

我有一个表单中的表格。我的其中一个文本框包含了html标签,例如:

</br>Phone: </br> 814-888-9999 </br> Email: </br> aaa@gmail.com.  

当我试图构建时,它会给我一个错误,说:

检测到来自客户端的潜在危险的 Request.Form 值 (QuestionAnswer="...ics Phone:<br/>814-888-9999<br...")

我尝试了 validation request="false" 但没有起作用。

抱歉,我还没有添加需要查看的html代码。 我正在提取一些问题进行编辑,如果需要的话。

 <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"   Inherits="System.Web.Mvc.ViewPage<dynamic>" %>


<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
EditFreqQuestionsUser
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<script type="text/javascript">
$(document).ready(function () {
    $("#freqQuestionsUserUpdateButton").click(function () {
        $("#updateFreqQuestionsUser").submit();
    });
});
</script>
<h2>Edit Freq Questions User </h2>

<%Administrator.AdminProductionServices.FreqQuestionsUser freqQuestionsUser =   ViewBag.freqQuestionsUser != null ? ViewBag.freqQuestionsUser : new   Administrator.AdminProductionServices.FreqQuestionsUser(); %>
<%List<string> UserRoleList = Session["UserRoles"] != null ? (List<string>)Session["UserRoles"] : new List<string>(); %>
<form id="updateFreqQuestionsUser" action="<%=Url.Action("SaveFreqQuestionsUser","Prod")%>" method="post" onsubmit+>
<table> 
    <tr>
        <td colspan="3" class="tableHeader">Freq Questions User Details <input type ="hidden" value="<%=freqQuestionsUser.freqQuestionsUserId%>" name="freqQuestionsUserId"/> </td>
    </tr>
     <tr>
        <td colspan="2" class="label">Question Description:</td>
        <td class="content">
            <input type="text" maxlength="2000" name="QuestionDescription" value="  <%=freqQuestionsUser.questionDescription%>" />
        </td>
    </tr>
     <tr>
        <td colspan="2" class="label">QuestionAnswer:</td>
        <td class="content">
            <input type="text" maxlength="2000" name="QuestionAnswer" value="<%=freqQuestionsUser.questionAnswer%>" />
        </td>
    </tr>
    <tr>
        <td colspan="3" class="tableFooter">
                <br />
                <a id="freqQuestionsUserUpdateButton" href="#" class="regularButton">Save</a>
                <a href="javascript:history.back()" class="regularButton">Cancel</a>
        </td> 
    </tr>
    </table>
      </form>
</asp:Content>
7个回答

32

在提交页面之前,您需要使用 window.escape(...) 对文本框的值进行 HTML 编码。

如果您需要在服务器端使用未编码的文本,则可以使用 HttpUtility.UrlDecode(...) 方法。

非常快速的示例:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="SO.WebForm1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script>
        function makeSafe() {
            document.getElementById('TextBox1').value = window.escape(document.getElementById('TextBox1').value);
        };

        function makeDangerous() {
            document.getElementById('TextBox1').value = window.unescape(document.getElementById('TextBox1').value);
        }
    </script>
</head>
<body>
    <form id="form1" runat="server" onsubmit="makeSafe();">
    <div>
        <asp:TextBox ID="TextBox1" runat="server" TextMode="MultiLine" Rows="10" ClientIDMode="Static"></asp:TextBox>
    </div>
    <asp:Button ID="Button1" runat="server" Text="Button" />
    </form>


     <script>
         makeDangerous();
    </script>
</body>
</html>

请按以下方式修改您的代码:

<script type="text/javascript">
    $(document).ready(function () {
        makeDangerous();
        $("#freqQuestionsUserUpdateButton").click(function () {
            makeSafe();
            $("#updateFreqQuestionsUser").submit();
        });
    });

    // Adding an ID attribute to the inputs you want to validate is simplest
    // Better would be to use document.getElementsByTagName and filter the array on NAME
    // or use a JQUERY select....

    function makeSafe() {
        document.getElementById('QuestionAnswer').value = window.escape(document.getElementById('QuestionAnswer').value);
    };

    // In this case adding the HTML back to a textbox should be 'safe'
    // You should be very wary though when you use it as actual HTML
    // You MUST take steps to ensure the HTML is safe.
    function makeDangerous() {
        document.getElementById('QuestionAnswer').value = window.unescape(document.getElementById('QuestionAnswer').value);
    }
</script>

14

使用[ValidateInput]属性装饰控制器操作:

[ValidateInput(false)]
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
    ...
}

12
虽然这是一个正确的答案,但并不是一个好的答案。我认为建议用户盲目禁用验证并不是个好主意。相反,他们应该确定为什么验证失败,然后想出一种解决方案,而不是只是关闭一个重要的安全机制。像 Ted SpenceAdrian 提供的建议是一个不错的起点。 - Kevin Babcock
2
@KevinBabcock,“重要的安全机制”?我对此感到好笑。您能否解释一下这个验证有什么重要性?而且,您只是针对特定操作禁用了验证。在ASP.NET MVC 3中,您可以通过使用“[AllowHtml]”属性装饰您的视图模型属性来实现更精细的逻辑。在提交之前使用JavaScript转义用户输入的意义何在? - Darin Dimitrov
6
认真的吗?你认为输入验证是可笑和不重要的吗?"未正确中和"用户输入已经被列在当今软件安全漏洞前 25 名错误之列。你刚刚告诉那个人要完全禁用输入验证,而没有任何解释。没有解释,也没有提供替代方案。 - Kevin Babcock
2
在提交用户输入到服务器之前使用编码的目的是什么?请查看[XSS预防规则#2](http://bit.ly/4U6fcT)。显然,这只是第一道防线,还应该在服务器上验证输入。 - Kevin Babcock
3
这是旧的,但@DarinDimitrov是正确的,我知道如何验证自己的输入,这里我特别想允许HTML - 就像标题所说的那样。 - Scott Selby
显示剩余4条评论

3

客户端JavaScript:

function codificarTags() 
{
     document.getElementById('txtDescripcion').value = document.getElementById('txtDescripcion').value.replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
    
<form id="form1" runat="server" onsubmit="codificarTags();">

服务器:

protected void Page_Load(object sender, EventArgs e)
{
     txtDescripcion.Text = txtDescripcion.Text.Replace(@"&lt;", @"<").Replace(@"&gt;", @">");
}

1

0
我采用了不同的方法。我想在我的应用程序中广泛使用HTML文本框。我创建了一个用户控件,可以避免每次添加新控件时编辑JavaScript。我的整个控件非常自定义,但是HTML处理的核心如下所示。 < p > UserControl标记具有一些简单的JavaScript来转义和取消转义文本框。

<script type="text/javascript">

    function UnescapeControl(clientId) {
            $('#' + clientId).val(window.unescape($('#' + clientId).val()));
    }

    function EscapeAllControls() {
       var escapeControList = JSON.parse('<%= new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(EscapeControlList) %>');
       for (var i = 0; i < escapeControList.length; i++) 
           EscapeControl(escapeControList[i]);            
    }

    function EscapeControl(textClientId) {
       document.getElementById(textClientId).value = window.escape(document.getElementById(textClientId).value); 
    }
</script>

<asp:TextBox ID="Txt_SavableText" CssClass="form-control" Width="100%" runat="server" ></asp:TextBox>

代码后台负责使用RegisterOnSubmitStatement在回传之前对控件进行转义,并在回传后使用RegisterStartupScript对其进行反转义。

public partial class SavableTextBox : System.Web.UI.UserControl
{

    public List<string> EscapeControlList
    {
        get
        {
            if (Session["STB_EscapeControlList"] == null)
                Session["STB_EscapeControlList"] = new List<string>();
            
            return (List<string>)Session["STB_EscapeControlList"];
        }
        set { Session["STB_EscapeControlList"] = value; }
    }

    

    protected void Page_Load(object sender, EventArgs e)
    {
        if (EscapeHtmlOnPostback && !EscapeControlList.Contains(GetClientId()))
            EscapeControlList.Add(GetClientId());

        // When using a script manager, you should use ScriptManager instead of ClientScript.
        if (EscapeHtmlOnPostback)
            ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "UnescapeControl_" + GetClientId(), "UnescapeControl('" + GetClientId() + "');", true);

        // Ensure we have our escape script called before all post backs containing escapable controls.
        // This is like calling OnClientClick before everything.
        if (EscapeControlList != null && EscapeControlList.Count > 0)
            this.Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), "SaveableTextBoxEscaper", "EscapeAllControls();");
        
    }


   public string Text
    {
        get
        {
            return Txt_SavableText.Text;
        }
        set
        {
            Txt_SavableText.Text = value;
        }
    }

    public string GetClientId()
    {
        return Txt_SavableText.ClientID;
    }
}

现在我们可以在设置 EscapeHtmlOnPostback="True" 的情况下随处使用它。

<%@ Register TagPrefix="STB" TagName="SavableTextBox" Src="~/SavableTextBox.ascx" %>
<STB:SavableTextBox ID="Txt_HtmlTextBox" EscapeHtmlOnPostback="True" runat="server" />

请注意,当我们在回传期间访问 Txt_HtmlTextBox.Text 时,它已经被转义处理了。

0

在文本框中使用HTML并不是一个好的做法,也许可以使用换行符(Environment.NewLine)或者\r\n代替br?

.NET参考资料

示例(使用C#):

textBox1.Multiline = true;
textBox1.Text = "test" + Environment.NewLine + "test2";

我可以在文本框中使用 /n 或 /r 吗? - Yusuf
我在文本框中输入了 \n,但它没有起作用。它也没有给我任何错误提示。它只是输入了 \n,所以现在就像是 \n814-888-9999。 - Yusuf
@Yusuf 如果文本框的类型是多行,那么它就可以了!使用 text1.text= "test" + "\r\n" + "test2"; - XhkUnlimit
抱歉,我颠倒了 \n 和 \r 的顺序,应该是 \r\n 才能正常工作。更改 freqQuestionsUser.questionAnswer 就可以了。 - XhkUnlimit

0
我喜欢@clamchoda的用户控件解决方案,并对其进行了一些尝试。正如他所提到的,他的整个控件非常自定义化,代码反映了更复杂的需求。如果你想要更简单的东西,可以考虑以下内容。
请注意,在这个版本中,文本在PostBack时会自动解码,因此您的页面代码不需要(就像使用标准的TextBox控件一样)。
用户控件的HTML标记:
<script>
   document.forms[0].addEventListener("submit", function (e){
      let txtBox = document.getElementById('<%= TxtBox.ClientID %>')
      txtBox.value = window.escape(txtBox.value);
   });
</script>

<asp:TextBox ID="TxtBox" runat="server" />

代码后台(使用VB编写,但很容易转换为C#):
Public Property Text As String
   Get
      Return TxtBox.Text
   End Get
   Set
      TxtBox.Text = Value
   End Set
End Property

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

   If IsPostBack Then
      TxtBox.Text = Uri.UnescapeDataString(TxtBox.Text)
   End If

End Sub

以上是基本的内容。为了允许在HTML中进行标记,我添加了以下属性(从最有用的开始,逐渐添加其他属性以满足特定需求)。
Public Property CssClass As String
   Get
      Return TxtBox.CssClass
   End Get
   Set
      TxtBox.CssClass= Value
   End Set
End Property

Public Property StyleMargin() As String
   'Note: use this to set all four margins.
   Get
      Return TxtBox.Style("margin")
   End Get
   Set(ByVal value As String)
      TxtBox.Style.Add("margin", value)
   End Set
End Property

Public Property Width As String
   Get
      Return TxtBox.Width
   End Get
   Set
      TxtBox.Width= Unit.Parse(value)
   End Set
End Property

最后,尽管违反了所有最佳实践,但要在代码后台访问完整的TextBox属性,您可以使用以下方法:
Public ReadOnly Property TxtBoxControl() As TextBox
   '==>   USE RESPONSIBLY !!!   <===
   Get
      Return TxtBox
   End Get
End Property

正如已经注意到的那样,在页面上使用它,您需要在HTML标记中注册用户控件:

<%@ Register TagPrefix="STB" TagName="PostBackSafeTextBox" Src="~/PostBackSafeTextBox.ascx" %>

<STB:PostBackSafeTextBox ID="MyTextBoxTxt" runat="server" />

我希望以上的内容对其他人有所帮助。话虽如此,这个创意首先要归功于@clamchoda。非常感谢@clamchoda —— 这真的帮了我大忙!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接