所以我建立了一个页面,人们可以在这里提交教程。这些教程基本上是由TinyMCE编辑器构建的。
无论如何,人们可以滥用它,只发布自己的非转义文本,并插入一些恶意的<script>
。
所以我的问题是:用正则表达式删除<script>
标签是否足够安全?在存储之前,我会在后台运行这个正则表达式
我发现了这个表达式,例如
<scriptb[^<]*(?:(?!</script>)<[^<]*)*</script>
否。他们可能会使用多字节字符来绕过您的regexp,或者偷偷地使用不匹配的开始和结束标记的组合,创建虚假的结束脚本标记,在属性中引用它们,等等…不要试图用RegEx解析潜在的嘈杂/格式错误的HTML,请使用专为处理这些问题而设计的HTML解析引擎。关于用正则表达式解析HTML的著名答案如下:regex匹配除XHTML自包含标记之外的开放标记
如果你正在寻找一个,我发誓这个PHP库:http://simplehtmldom.sourceforge.net/
它首先通过将噪声转换为实体来清理文档,然后再考虑"噪声";脚本""风格";,以及";文本区域";元素,在打开和关闭标记之间找到的任何内容都是文本而不是HTML。然后,它将结果解析为DOM结构,可以像在JavaScript中使用DOM方法解析文档一样进行解析。它带有一个";保存";方法,(这将产生字符串),所以在剥离页面中的标记后,您将得到修改后的格式良好的文档。我也用大数据测试过这个库,当我之前用大数据使用regexp时,由于regexp达到了PHP内存限制,所以它失败了,这个库解析这些文档时没有内存问题。因此,我已经对它进行了非常彻底的测试,并在大型项目中使用过它,它从未让我失望——就像内置的PHP函数/类具有格式错误的数据一样。
编辑:下面是一个如何打破它的例子:
<scr<script>ipt></scr</script>ipt>alert('XSS!')</script>
仅仅因为jQuery使用了regex,并不能保证它对服务器的安全。
即使您使用";gi";标志,无所谓:
var str="<scr<script>ipt></scr</script>ipt>alert('XSS!')</script>";
str=str.replace(/<scriptb[^<]*(?:(?!</script>)<[^<]*)*</script>/gi,'');
//the "g" flag doesn't help here since you need to start from the beginning, not continue in the middle
alert(str);
但如果你在循环中使用它,而不是用";g";flag,你会处理掉我提起的这个案子。
编辑2:如果目的是从所有JavaScript问题中清除用户输入;"加载";以及";onclick";属性,为什么要重新发明轮子?有http://htmlpurifier.org/(参见演示)
为什么不使用DOM来代替regex呢?
$content = "<h1>title</h1><p> test <span>1<!-- regular comment --><script> my script</script></span><script> my script</script></p><script> my script</script> <!--[if IE]><script>alert('XSS');</script><![endif]-->";
// creates a DOMDocument based on your string (without doctype, html and another extra tags), and wraps it in a div
$dom = new DOMDocument();
$dom->loadHTML("<div>{$content}</div>", LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED);
//Removing any comments or conditional comments
$xpath = new DOMXPath($dom);
foreach ($xpath->query('//comment()') as $comment) {
$comment->parentNode->removeChild($comment);
}
// function to remove any tag
function verifyNodes(DOMNode $node) {
$removedTags = ['script', 'iframe']; // what tags i want to remove
foreach ($node->childNodes as $childNode)
{
if (in_array($childNode->nodeName, $removedTags)) {
$childNode->parentNode->removeChild($childNode);
} elseif ($childNode->hasChildNodes()) {
verifyNodes($childNode);
}
}
}
// calling verifyNodes
verifyNodes($dom);
// get all the content of my first div, and print it
$newContent = $dom->getElementsByTagName('div')->item(0);
foreach ($newContent->childNodes as $childNode) {
var_dump($dom->saveHTML($childNode));
}
就像我使用nodeName来验证标记的名称一样,如果我们想删除其他内容(查看节点XML常量列表),我们也可以使用nodeType。
如果您可以使用支持原子组的引擎,这可能会
工作这将最接近于浏览器如何解析脚本
标签。
查找:(?><script(?:(?:s+(?:"[Ss]*?"|'[Ss]*?'|[^>]*?)+)|/)>)(?<=/>)|(?><script(?:s+(?:"[Ss]*?"|'[Ss]*?'|[^>]*?)+)?>)(?<!/>)[Ss]*?</scripts*>
替换:空字符串
格式化:
# If script tags can be <script .... />
(?>
<
script
(?:
(?:
s+
(?: " [Ss]*? " | ' [Ss]*? ' | [^>]*? )+
)
| /
)
>
)
(?<= /> )
|
# Or, if script tags with content can be <script .... > ... </script>
(?>
<
script
(?:
s+
(?: " [Ss]*? " | ' [Ss]*? ' | [^>]*? )+
)?
>
)
(?<! /> )
[Ss]*?
</script s* >