如何使用 C 验证字符串是否不包含 HTML#

How to validate that a string doesn't contain HTML using C#

提问人:Ben Mills 提问时间:10/15/2008 更新时间:5/19/2020 访问量:53563

问:

有没有人有一种简单有效的方法来检查字符串是否不包含 HTML?基本上,我想检查某些字段是否只包含纯文本。我想过寻找<字符,但这可以很容易地在纯文本中使用。另一种方法是使用以下方法创建新的 System.Xml.Linq.XElement:

XElement.Parse("<wrapper>" + MyString + "</wrapper>")

并检查 XElement 是否不包含子元素,但对于我需要的东西来说,这似乎有点重量级。

C# HTML 验证

评论

0赞 Rob 10/15/2008
要做到这一点,你可能必须定义你所说的“HTML”和“纯文本”的含义,例如:你是否允许某人在纯文本中放置“<G>”,它看起来像一个HTML元素,但不是,以及你允许哪些字符。
0赞 Ben Mills 10/16/2008
就我而言,我完全不说标签,所以不允许使用<G>。我的用户是将产品输入我们公司网站的有限数量的员工。他们已经开始滥用这些字段,并在未设计为包含 HTML 的字段中包含 HTML。

答:

68赞 ICR 10/15/2008 #1

以下内容将与任何匹配的标签集匹配。即 <b>this</b>

Regex tagRegex = new Regex(@"<\s*([^ >]+)[^>]*>.*?<\s*/\s*\1\s*>");

以下内容将与任何单个标签匹配。即 <b>(它不必关闭)。

Regex tagRegex = new Regex(@"<[^>]+>");

然后,您可以像这样使用它

bool hasTags = tagRegex.IsMatch(myString);

评论

0赞 Jeroen K 10/15/2018
第二个匹配“a < b, b > c”
0赞 Skepti_Capy 10/27/2021
我喜欢第一个,但它不包含重复匹配的情况。例如,像“<b>text<i>text</b>text<b>text</i></b>”这样的字符串,它只会匹配“<b>text<i>text</b>”和“<b>text</i></b>”,与重叠的“<i>text</b>text<b>text</i>”
15赞 Josef 10/15/2008 #2

给你:

using System.Text.RegularExpressions;
private bool ContainsHTML(string checkString)
{
  return Regex.IsMatch(checkString, "<(.|\n)*?>");
}

这是最简单的方法,因为括号中的项目不太可能自然出现。

评论

0赞 ripvlan 2/2/2021
括号不太可能自然出现?!我不跟着。如果有人输入“if x < 0 or y > 10”,这个正则表达式将捕获“< 0 或 y >” 然而,我的例子中没有 HTML。正则表达式作为 HTML 解析器通常不受欢迎。
2赞 DOK 10/15/2008 #3

尖括号可能不是您唯一的挑战。其他字符也可能是可能有害的脚本注入。比如常见的双连字符“--”,也可以用在SQL注入中。还有其他人。

在 ASP.Net 页上,如果 machine.config、web.config 或 page 指令中的 validateRequest = true,则在检测到 HTML 标记或其他各种潜在的脚本注入攻击时,用户将收到一个错误页面,指出“从客户端检测到潜在危险的 Request.Form 值”。您可能希望避免这种情况,并提供更优雅、更不可怕的 UI 体验。

您可以使用正则表达式测试开始和结束标记<>,如果只有一个文本占用,则允许文本。允许<或>,但不允许<后跟一些文本,然后按此顺序>。

您可以允许使用尖括号,并在保留数据时对文本进行 HtmlEncode 编码以保留它们。

评论

0赞 Robert Rossney 10/16/2008
如果你处理SQL注入的策略是从输入中剥离“--”,那么你就有更大的问题了。
1赞 DOK 10/17/2008
很好的观点,Robert,但我不认为这是全面解释防御SQL注入或其他脚本注入技术的地方。我抵御 SQL 注入的第一道防线是使用参数化 SQL。你的是什么?
24赞 J c 10/15/2008 #4

您可以通过使用 HttpUtility.HtmlEncode 对输入进行编码来确保纯文本。

事实上,根据你希望检查的严格程度,你可以用它来确定字符串是否包含 HTML:

bool containsHTML = (myString != HttpUtility.HtmlEncode(myString));

评论

2赞 Eric Fan 8/13/2013
一个简单而有效的答案!
13赞 PeteG 10/3/2014
不幸的是,如果您的字符串包含撇号、与号等,则不起作用
0赞 J c 10/5/2014
@PeteG 好点,是的,似乎从 .NET 4 开始,此方法实际上编码的内容比以前更多,例如单引号。这使得这种技术不太有用。
0赞 Sreejith K. 11/14/2019
这表示文本“abcd<”包含html
0赞 Orhano95 12/23/2022
如果你在字符串上添加无意义的额外编码字符,method 将返回 true。对于安全检查来说风险太大。
8赞 Ben Mills 10/16/2008 #5

我刚刚尝试了我的 XElement.Parse 解决方案。我在字符串类上创建了一个扩展方法,以便可以轻松重用代码:

public static bool ContainsXHTML(this string input)
{
    try
    {
        XElement x = XElement.Parse("<wrapper>" + input + "</wrapper>");
        return !(x.DescendantNodes().Count() == 1 && x.DescendantNodes().First().NodeType == XmlNodeType.Text);
    }
    catch (XmlException ex)
    {
        return true;
    }
}

我发现的一个问题是纯文本 & 号和小于字符会导致 XmlException 并指示该字段包含 HTML(这是错误的)。为了解决这个问题,传入的输入字符串首先需要将 & 号和小于 号字符转换为其等效的 XHTML 实体。我编写了另一种扩展方法来做到这一点:

public static string ConvertXHTMLEntities(this string input)
{
    // Convert all ampersands to the ampersand entity.
    string output = input;
    output = output.Replace("&amp;", "amp_token");
    output = output.Replace("&", "&amp;");
    output = output.Replace("amp_token", "&amp;");

    // Convert less than to the less than entity (without messing up tags).
    output = output.Replace("< ", "&lt; ");
    return output;
}

现在,我可以获取用户提交的字符串,并使用以下代码检查它是否不包含 HTML:

bool ContainsHTML = UserEnteredString.ConvertXHTMLEntities().ContainsXHTML();

我不确定这是否是防弹的,但我认为这对我的情况来说已经足够了。

评论

0赞 Robert Rossney 10/16/2008
您正在检查以确保它不包含 XHTML。您没有检查以确保它不包含 HTML,而 HTML 不一定是格式正确的 XML。此外,您的代码不会捕获“<b></b><b>this is XHTML</b>”。
0赞 Ben Mills 10/17/2008
实际上,格式不正确的旧式 HTML XML 将导致 XElement.Parse 方法失败。我的方法假定 Parse 方法失败意味着字符串包含某种形式的 HTML。我想我的代码真的在寻找任何形式的标签。
0赞 1/22/2013
我们还可能使用正则表达式来检查开始结束标签。
0赞 Mark 3/12/2011 #6

使用上面提到的 HttpUtility.HtmlEncode 方法时要小心。如果您检查某些带有特殊字符的文本,而不是 HTML,则它将错误地计算。也许这就是为什么 J c 使用“......取决于你希望检查的严格程度......”

7赞 kns98 12/13/2014 #7

这还会检查诸如< BR /> 带有可选空格的自封闭标签之类的内容。该列表不包含新的 HTML5 标记。

internal static class HtmlExts
{
    public static bool containsHtmlTag(this string text, string tag)
    {
        var pattern = @"<\s*" + tag + @"\s*\/?>";
        return Regex.IsMatch(text, pattern, RegexOptions.IgnoreCase);
    }

    public static bool containsHtmlTags(this string text, string tags)
    {
        var ba = tags.Split('|').Select(x => new {tag = x, hastag = text.containsHtmlTag(x)}).Where(x => x.hastag);

        return ba.Count() > 0;
    }

    public static bool containsHtmlTags(this string text)
    {
        return
            text.containsHtmlTags(
                "a|abbr|acronym|address|area|b|base|bdo|big|blockquote|body|br|button|caption|cite|code|col|colgroup|dd|del|dfn|div|dl|DOCTYPE|dt|em|fieldset|form|h1|h2|h3|h4|h5|h6|head|html|hr|i|img|input|ins|kbd|label|legend|li|link|map|meta|noscript|object|ol|optgroup|option|p|param|pre|q|samp|script|select|small|span|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|ul|var");
    }
}