捕获 XSS(跨站点脚本)攻击的最佳正则表达式(在 Java 中)?

Best regex to catch XSS (Cross-site Scripting) attack (in Java)?

提问人:Thierry Roy 提问时间:8/24/2008 最后编辑:CommunityThierry Roy 更新时间:11/13/2022 访问量:98679

问:

Jeff 实际上在 Sanitize HTML 中发布了有关此内容的信息。但他的例子是用 C# 编写的,我实际上对 Java 版本更感兴趣。有没有人有更好的 Java 版本?他的示例是否足够好,可以直接从 C# 转换为 Java?

[更新]我对这个问题给予了赏金,因为当我问这个问题时,SO 并不像今天那样受欢迎 (*)。至于任何与安全有关的事情,研究得越多越好!

(*)事实上,我认为它仍处于封闭测试阶段

Java HTML 正则表达式 XSS

评论

0赞 Michael Myers 2/11/2009
你还需要什么?答案在我看来不错。

答:

0赞 svrist 8/26/2008 #1

使用 jeffs 代码的最大问题是目前不可用的 @。

如果需要,我可能会从 jeffs 代码中获取“原始”正则表达式并将其粘贴到

http://www.cis.upenn.edu/~matuszek/General/RegexTester/regex-tester.html

看到需要逃脱的东西被逃脱,然后使用它。


考虑到这个正则表达式的使用,我个人会确保我确切地理解我在做什么,为什么以及如果我没有成功会有什么后果,在复制/粘贴任何东西之前,就像其他答案试图帮助你一样。

(对于任何复制/粘贴来说,这都是相当合理的建议)

4赞 Emmanuel Rodriguez 2/11/2009 #2

我不相信使用正则表达式是查找所有可疑代码的最佳方法。正则表达式很容易被欺骗,尤其是在处理损坏的 HTML 时。例如,“清理 HTML”链接中列出的正则表达式将无法删除所有在元素名称和属性“href”之间具有属性的“a”元素:

< alt=“xss 注入” href=“http://www.malicous.com/bad.php” >

删除恶意代码的一种更可靠的方法是依赖于 XML 解析器,该解析器可以处理所有类型的 HTML 文档(Tidy、TagSoup 等),并使用 XPath 表达式选择要删除的元素。一旦 HTML 文档被解析为 DOM 文档,就可以轻松安全地找到要 revome 的元素。使用 XSLT 甚至很容易做到这一点。

评论

0赞 Chase Seibert 2/11/2009
+1,请参阅我对真实世界 Java API 的响应,该 API 正是这样做的
81赞 Chase Seibert 2/11/2009 #3

不要使用正则表达式执行此操作。请记住,您不仅仅是在保护有效的 HTML;您正在防止 Web 浏览器创建的 DOM。浏览器可以很容易地被欺骗从无效的 HTML 生成有效的 DOM。

例如,请参阅此混淆 XSS 攻击列表。您是否准备好定制正则表达式来防止 IE6/7/8 上对 Yahoo 和 Hotmail 的这种真实世界攻击?

<HTML><BODY>
<?xml:namespace prefix="t" ns="urn:schemas-microsoft-com:time">
<?import namespace="t" implementation="#default#time2">
<t:set attributeName="innerHTML" to="XSS&lt;SCRIPT DEFER&gt;alert(&quot;XSS&quot;)&lt;/SCRIPT&gt;">
</BODY></HTML>

这种在IE6上有效的攻击怎么样?

<TABLE BACKGROUND="javascript:alert('XSS')">

本网站上未列出的攻击怎么办?Jeff 方法的问题在于,它不像声称的那样是白名单。正如该页面上的某个人熟练地指出的那样:

它的问题是 html 必须干净。在某些情况下, 你可以传入被黑的 HTML,它 不会匹配它,在这种情况下它会 返回被黑的 HTML 字符串,因为它 不会匹配任何要替换的东西。这 不是严格意义上的白名单。

我建议使用像 AntiSmy 这样的专用工具。它的工作原理是实际解析 HTML,然后遍历 DOM 并删除任何不在可配置白名单中的内容。主要区别在于能够优雅地处理格式错误的 HTML。

最好的部分是它实际上对上述站点上的所有 XSS 攻击进行了单元测试。此外,还有什么比这个 API 调用更容易的:

public String toSafeHtml(String html) throws ScanException, PolicyException {

    Policy policy = Policy.getInstance(POLICY_FILE);
    AntiSamy antiSamy = new AntiSamy();
    CleanResults cleanResults = antiSamy.scan(html, policy);
    return cleanResults.getCleanHTML().trim();
}

评论

0赞 Emmanuel Rodriguez 2/11/2009
AntiSamy看起来很棒!此外,使用不同的策略也是一个好主意,因为它将清理规则保留在代码之外,使其更易于维护。这显然是一个非常好的方法。荣誉。
2赞 bobince 2/11/2009
+1.您无法使用正则表达式可靠地处理 HTML。将其解析为易于过滤的 DOM,然后使用已知良好的序列化,是迄今为止更明智的方法。
0赞 Thierry Roy 2/11/2009
我真的很喜欢这个答案,因为它没有直接回答问题,而是解决问题!
0赞 Kapocsi 9/23/2018
你的第二个链接已经死了。
0赞 Brian 2/12/2009 #4

[\s\w\.]*.如果不匹配,则表示您有 XSS。或。请注意,此表达式只允许使用字母、数字和句点。出于对 XSS 的恐惧,它避免了所有符号,甚至是有用的符号。一旦你允许&,你就有烦恼了。仅仅用 & 替换所有实例是不够的。太复杂了,不能相信:P。显然,这将禁止大量合法文本(您可以用 ! 或其他东西替换所有不匹配的字符),但我认为它会杀死 XSS。&amp;

将其解析为 html 并生成新 html 的想法可能会更好。

3赞 atoms 6/2/2009 #5
^(\s|\w|\d|<br>)*?$ 

这将验证字符、数字、空格以及标签。 如果您想要更多风险,可以添加更多标签,例如<br>

^(\s|\w|\d|<br>|<ul>|<\ul>)*?$
7赞 user3709489 6/7/2014 #6

我从NoScript中提取了最好的Anti-XSS插件,这是它的正则表达式: 完美无瑕的工作:

<[^\w<>]*(?:[^<>"'\s]*:)?[^\w<>]*(?:\W*s\W*c\W*r\W*i\W*p\W*t|\W*f\W*o\W*r\W*m|\W*s\W*t\W*y\W*l\W*e|\W*s\W*v\W*g|\W*m\W*a\W*r\W*q\W*u\W*e\W*e|(?:\W*l\W*i\W*n\W*k|\W*o\W*b\W*j\W*e\W*c\W*t|\W*e\W*m\W*b\W*e\W*d|\W*a\W*p\W*p\W*l\W*e\W*t|\W*p\W*a\W*r\W*a\W*m|\W*i?\W*f\W*r\W*a\W*m\W*e|\W*b\W*a\W*s\W*e|\W*b\W*o\W*d\W*y|\W*m\W*e\W*t\W*a|\W*i\W*m\W*a?\W*g\W*e?|\W*v\W*i\W*d\W*e\W*o|\W*a\W*u\W*d\W*i\W*o|\W*b\W*i\W*n\W*d\W*i\W*n\W*g\W*s|\W*s\W*e\W*t|\W*i\W*s\W*i\W*n\W*d\W*e\W*x|\W*a\W*n\W*i\W*m\W*a\W*t\W*e)[^>\w])|(?:<\w[\s\S]*[\s\0\/]|['"])(?:formaction|style|background|src|lowsrc|ping|on(?:d(?:e(?:vice(?:(?:orienta|mo)tion|proximity|found|light)|livery(?:success|error)|activate)|r(?:ag(?:e(?:n(?:ter|d)|xit)|(?:gestur|leav)e|start|drop|over)?|op)|i(?:s(?:c(?:hargingtimechange|onnect(?:ing|ed))|abled)|aling)|ata(?:setc(?:omplete|hanged)|(?:availabl|chang)e|error)|urationchange|ownloading|blclick)|Moz(?:M(?:agnifyGesture(?:Update|Start)?|ouse(?:PixelScroll|Hittest))|S(?:wipeGesture(?:Update|Start|End)?|crolledAreaChanged)|(?:(?:Press)?TapGestur|BeforeResiz)e|EdgeUI(?:C(?:omplet|ancel)|Start)ed|RotateGesture(?:Update|Start)?|A(?:udioAvailable|fterPaint))|c(?:o(?:m(?:p(?:osition(?:update|start|end)|lete)|mand(?:update)?)|n(?:t(?:rolselect|extmenu)|nect(?:ing|ed))|py)|a(?:(?:llschang|ch)ed|nplay(?:through)?|rdstatechange)|h(?:(?:arging(?:time)?ch)?ange|ecking)|(?:fstate|ell)change|u(?:echange|t)|l(?:ick|ose))|m(?:o(?:z(?:pointerlock(?:change|error)|(?:orientation|time)change|fullscreen(?:change|error)|network(?:down|up)load)|use(?:(?:lea|mo)ve|o(?:ver|ut)|enter|wheel|down|up)|ve(?:start|end)?)|essage|ark)|s(?:t(?:a(?:t(?:uschanged|echange)|lled|rt)|k(?:sessione|comma)nd|op)|e(?:ek(?:complete|ing|ed)|(?:lec(?:tstar)?)?t|n(?:ding|t))|u(?:ccess|spend|bmit)|peech(?:start|end)|ound(?:start|end)|croll|how)|b(?:e(?:for(?:e(?:(?:scriptexecu|activa)te|u(?:nload|pdate)|p(?:aste|rint)|c(?:opy|ut)|editfocus)|deactivate)|gin(?:Event)?)|oun(?:dary|ce)|l(?:ocked|ur)|roadcast|usy)|a(?:n(?:imation(?:iteration|start|end)|tennastatechange)|fter(?:(?:scriptexecu|upda)te|print)|udio(?:process|start|end)|d(?:apteradded|dtrack)|ctivate|lerting|bort)|DOM(?:Node(?:Inserted(?:IntoDocument)?|Removed(?:FromDocument)?)|(?:CharacterData|Subtree)Modified|A(?:ttrModified|ctivate)|Focus(?:Out|In)|MouseScroll)|r(?:e(?:s(?:u(?:m(?:ing|e)|lt)|ize|et)|adystatechange|pea(?:tEven)?t|movetrack|trieving|ceived)|ow(?:s(?:inserted|delete)|e(?:nter|xit))|atechange)|p(?:op(?:up(?:hid(?:den|ing)|show(?:ing|n))|state)|a(?:ge(?:hide|show)|(?:st|us)e|int)|ro(?:pertychange|gress)|lay(?:ing)?)|t(?:ouch(?:(?:lea|mo)ve|en(?:ter|d)|cancel|start)|ime(?:update|out)|ransitionend|ext)|u(?:s(?:erproximity|sdreceived)|p(?:gradeneeded|dateready)|n(?:derflow|load))|f(?:o(?:rm(?:change|input)|cus(?:out|in)?)|i(?:lterchange|nish)|ailed)|l(?:o(?:ad(?:e(?:d(?:meta)?data|nd)|start)?|secapture)|evelchange|y)|g(?:amepad(?:(?:dis)?connected|button(?:down|up)|axismove)|et)|e(?:n(?:d(?:Event|ed)?|abled|ter)|rror(?:update)?|mptied|xit)|i(?:cc(?:cardlockerror|infochange)|n(?:coming|valid|put))|o(?:(?:(?:ff|n)lin|bsolet)e|verflow(?:changed)?|pen)|SVG(?:(?:Unl|L)oad|Resize|Scroll|Abort|Error|Zoom)|h(?:e(?:adphoneschange|l[dp])|ashchange|olding)|v(?:o(?:lum|ic)e|ersion)change|w(?:a(?:it|rn)ing|heel)|key(?:press|down|up)|(?:AppComman|Loa)d|no(?:update|match)|Request|zoom))[\s\0]*=

测试:http://regex101.com/r/rV7zK8

我认为它阻止了 99% 的 XSS,因为它是 NoScript 的一部分,NoScript 是一个定期更新的插件

评论

0赞 KIC 9/9/2014
无法在 java 中编译:原因:java.util.regex.PatternSyntaxException:索引 525 附近的非法八进制转义序列
4赞 Jimbo Jonny 10/25/2016
我在 regex101 测试字符串中尝试了一个非常简单的 XSS 位,并在第一次尝试时破坏了它。不是很完美。我所做的只是添加一个带有 href 的链接。javascript:
0赞 KIC 9/9/2014 #7

一个旧线程,但也许这对其他用户有用。有一个用于 php 的维护安全层工具: https://github.com/PHPIDS/ 它基于一组正则表达式,您可以在此处找到该正则表达式:

https://github.com/PHPIDS/PHPIDS/blob/master/lib/IDS/default_filter.xml

1赞 Philip DiSarro 12/23/2018 #8

这个问题完美地说明了计算理论研究的一个很好的应用。计算理论是一个专注于产生和研究用于计算的数学表示的领域。

计算理论中一些最深刻的研究包括说明各种语言关系的证明。

计算理论家已经证明的一些语言关系包括:

enter image description here

这表明上下文无关语言严格比常规语言更强大。因此,如果一种语言是显式的、与上下文无关的(与上下文无关且不规则),那么任何正则表达式都不可能识别它。

JavaScript 至少是与上下文无关的,因此我们百分之百地知道,设计一个能够捕获所有 XSS 的正则表达式(正则表达式)在数学上是一项不可能完成的任务。

0赞 NoNaMe 9/7/2020 #9

对于 java,我使用了以下带有 replaceAll 的正则表达式,并为我工作

value.replaceAll("(?i)(\\b)(on\\S+)(\\s*)=|javascript:|(<\\s*)(\\/*)script|style(\\s*)=|(<\\s*)meta", "");

添加了 (?i) 以忽略字母的大小写。

0赞 Ashhh 11/8/2022 #10
public String validate(String value) {


    // Avoid anything between script tags
    Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");

    // Avoid anything in a src='...' type of expression
    scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");

    // Avoid anything in a src="..." type of expression
    scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");

    // Avoid anything in a src=... type of expression added because quotes are not necessary
    scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*(.*?)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");

    // Remove any lonesome </script> tag
    scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");

    // Remove any lonesome <script ...> tag
    scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");

    // Avoid eval(...) expressions
    scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");

    // Avoid expression(...) expressions
    scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");

    // Avoid javascript:... expressions
    scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");

    // Avoid vbscript:... expressions
    scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");

    // Avoid onload= expressions
    scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");

    // Avoid anything between script tags  added - paranoid regex.  note:  if testing local PREP this must be commented
    scriptPattern = Pattern.compile("<(.*?)[\r\n]*(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");

    //  Avoid anything between script tags  added - paranoid regex
    scriptPattern = Pattern.compile("<script(.*?)[\r\n]*(.*?)/script>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");

    // Avoid anything between * tags like *(alert)*  added
    scriptPattern = Pattern.compile("\\*(.*?)[\r\n]*(.*?)\\*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    
    // Avoid anything between + tags like +(alert)+  added
    scriptPattern = Pattern.compile("\\+(.*?)[\r\n]*(.*?)\\+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");

    // Prohibit lines containing = (...) added
    scriptPattern = Pattern.compile("=(.*?)\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    
    //  removing href link 
    scriptPattern = Pattern.compile("(?i)<[\\s]*[/]?[\\s]*a[^>]*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    //Avoid alert
    scriptPattern = Pattern.compile("alert", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");


    scriptPattern = Pattern.compile("[^\\dA-Za-z ]", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    
    
    return value;
            
}

评论

1赞 ruud 11/11/2022
这只是一个代码答案,没有进一步的解释。