如何过滤除特定白名单之外的所有 HTML 标签? [英] How do I filter all HTML tags except a certain whitelist?
问题描述
这是用于 .NET.设置了 IgnoreCase 而未设置 MultiLine.
This is for .NET. IgnoreCase is set and MultiLine is NOT set.
通常我擅长正则表达式,也许我的咖啡因不足...
Usually I'm decent at regex, maybe I'm running low on caffeine...
允许用户输入 HTML 编码的实体(<、<amp; 等),并使用以下 HTML 标签:
Users are allowed to enter HTML-encoded entities (<lt;, <amp;, etc.), and to use the following HTML tags:
u, i, b, h3, h4, br, a, img
自闭式 <br/>和<img/>允许有或没有额外空间,但不是必需的.
Self-closing <br/> and <img/> are allowed, with or without the extra space, but are not required.
我想:
- 去除以上所列之外的所有开始和结束 HTML 标签.
- 从剩下的标签中移除属性,除了锚点可以有一个href.
- Strip all starting and ending HTML tags other than those listed above.
- Remove attributes from the remaining tags, except anchors can have an href.
到目前为止我的搜索模式(替换为空字符串):
My search pattern (replaced with an empty string) so far:
<(?!i|b|h3|h4|a|img|/i|/b|/h3|/h4|/a|/img)[^>]+>
这似乎去除了除我想要的开始和结束标签之外的所有标签,但存在三个问题:
This seems to be stripping all but the start and end tags I want, but there are three problems:
- 必须包含每个允许标记的结束标记版本是很丑陋的.
- 属性继续存在.这会在一次更换中发生吗?
- 标签以开头,允许的标签名称会漏掉.例如,<abbrev>"和
- Having to include the end tag version of each allowed tag is ugly.
- The attributes survive. Can this happen in a single replacement?
- Tags starting with the allowed tag names slip through. E.g., "<abbrev>" and "<iframe>".
以下建议的模式不会去除没有属性的标签.
The following suggested pattern does not strip out tags that have no attributes.
</?(?!i|b|h3|h4|a|img)[^>]*>
如下所述,>"在属性值中是合法的,但可以肯定地说我不会支持.此外,不会有 CDATA 块等需要担心.只是一点点 HTML.
As mentioned below, ">" is legal in an attribute value, but it's safe to say I won't support that. Also, there will be no CDATA blocks, etc. to worry about. Just a little HTML.
Loophole 的回答是目前最好的,谢谢!这是他的模式(希望 PRE 更适合我):
Loophole's answer is the best one so far, thanks! Here's his pattern (hoping the PRE works better for me):
static string SanitizeHtml(string html)
{
string acceptable = "script|link|title";
string stringPattern = @"</?(?(?=" + acceptable + @")notag|[a-zA-Z0-9]+)(?:s[a-zA-Z0-9-]+=?(?:([""']?).*?1?)?)*s*/?>";
return Regex.Replace(html, stringPattern, "sausage");
}
我认为仍然可以对此答案进行一些小调整:
Some small tweaks I think could still be made to this answer:
我认为这可以修改为捕获简单的 HTML 注释(那些本身不包含标签的注释),方法是在可接受"变量中添加!--"并对表达式的末尾进行一些小的更改允许可选的尾随s--".
I think this could be modified to capture simple HTML comments (those that do not themselves contain tags) by adding "!--" to the "acceptable" variable and making a small change to the end of the expression to allow for an optional trailing "s--".
我认为如果属性之间有多个空白字符(例如:重格式 HTML,属性之间带有换行符和制表符),这会中断.
I think this would break if there are multiple whitespace characters between attributes (example: heavily-formatted HTML with line breaks and tabs between attributes).
编辑 2009-07-23: 这是我使用的最终解决方案(在 VB.NET 中):
Edit 2009-07-23: Here's the final solution I went with (in VB.NET):
Dim AcceptableTags As String = "i|b|u|sup|sub|ol|ul|li|br|h2|h3|h4|h5|span|div|p|a|img|blockquote"
Dim WhiteListPattern As String = "</?(?(?=" & AcceptableTags & _
")notag|[a-zA-Z0-9]+)(?:s[a-zA-Z0-9-]+=?(?:([""']?).*?1?)?)*s*/?>"
html = Regex.Replace(html, WhiteListPattern, "", RegExOptions.Compiled)
需要注意的是,A 标签的 HREF 属性仍然会被清除,这并不理想.
The caveat is that the HREF attribute of A tags still gets scrubbed, which is not ideal.
推荐答案
这是我为此任务编写的函数:
Here's a function I wrote for this task:
static string SanitizeHtml(string html)
{
string acceptable = "script|link|title";
string stringPattern = @"</?(?(?=" + acceptable + @")notag|[a-zA-Z0-9]+)(?:s[a-zA-Z0-9-]+=?(?:(["",']?).*?1?)?)*s*/?>";
return Regex.Replace(html, stringPattern, "sausage");
}
出于某种原因,我将之前的更正作为单独的答案发布,因此我将它们合并到这里.
For some reason I posted a correction to my previous answer as a separate answer, so I am consolidating them here.
我将解释一下正则表达式,因为它有点长.
I will explain the regex a bit, because it is a little long.
第一部分匹配一个左括号和 0 或 1 个斜线(以防它是一个结束标记).
The first part matches an open bracket and 0 or 1 slashes (in case it's a close tag).
接下来,您会看到一个 if-then 结构,并向前看.(?(?=SomeTag)then|else) 我正在检查字符串的下一部分是否是可接受的标签之一.您可以看到我将正则表达式字符串与可接受的变量连接起来,该变量是由垂直条分隔的可接受的标签名称,以便任何术语都匹配.如果匹配,你可以看到我输入了notag"这个词,因为没有标签可以匹配它,如果可以接受,我想不理会它.否则,我将转到 else 部分,在那里我匹配任何标签名称 [a-z,A-Z,0-9]+
Next you see an if-then construct with a look ahead. (?(?=SomeTag)then|else) I am checking to see if the next part of the string is one of the acceptable tags. You can see that I concatenate the regex string with the acceptable variable, which is the acceptable tag names seperated by a verticle bar so that any of the terms will match. If it is a match, you can see I put in the word "notag" because no tag would match that and if it is acceptable I want to leave it alone. Otherwise I move on to the else part, where i match any tag name [a-z,A-Z,0-9]+
接下来,我想匹配 0 个或多个属性,我假设这些属性的格式为 attribute="value".所以现在我将代表一个属性的这部分分组,但我使用 ?: 来防止这个组被捕获以提高速度: (?:s[az,AZ,0-9,-]+=?(?:(["",']?).?1?))
Next, I want to match 0 or more attributes, which I assume are in the form attribute="value". so now I group this part representing an attribute but I use the ?: to prevent this group from being captured for speed: (?:s[a-z,A-Z,0-9,-]+=?(?:(["",']?).?1?))
这里我从标记名和属性名之间的空白字符开始,然后匹配一个属性名:[a-z,A-Z,0-9,-]+
Here I begin with the whitespace character that would be between the tag and attribute names, then match an attribute name: [a-z,A-Z,0-9,-]+
接下来我匹配一个等号,然后匹配一个引号.我将引用分组以便将其捕获,稍后我可以进行反向引用 1 以匹配相同类型的引用.在这两个引号之间,您可以看到我使用句点来匹配任何内容,但是我使用的是惰性版本 *?而不是贪婪版本 * 以便它只匹配将结束该值的下一个引用.
next I match an equals sign, and then either quote. I group the quote so it will be captured, and I can do a backreference later 1 to match the same type of quote. In between these two quotes, you can see I use the period to match anything, however I use the lazy version *? instead of the greedy version * so that it will only match up to the next quote that would end this value.
接下来我们在用括号关闭组后放一个 * 以便它匹配多个属性/值组合(或没有).最后,我们将一些空格与 s 和 0 或 1 个结束斜杠匹配在标签中,用于 xml 样式的自关闭标签.
next we put a * after closing the groups with parenthesis so that it will match multiple attirbute/value combinations (or none). Last we match some whitespace with s, and 0 or 1 ending slashes in the tag for xml style self closing tags.
你可以看到我正在用香肠替换标签,因为我饿了,但你也可以用空字符串替换它们来清除它们.
You can see I'm replacing the tags with sausage, because I'm hungry, but you could replace them with empty string too to just clear them out.
这篇关于如何过滤除特定白名单之外的所有 HTML 标签?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!