XSS 攻击有两⼤要素:
针对第⼀个要素:我们是否能够在⽤户输⼊的过程,过滤掉⽤户输⼊的恶意代码呢?
在⽤户提交时,由前端过滤输⼊,然后提交到后端。这样做是否可⾏呢?
答案是不可⾏。⼀旦攻击者绕过前端过滤,直接构造请求,就可以提交恶意代码了。
那么,换⼀个过滤时机:后端在写⼊数据库前,对输⼊进⾏过滤,然后把“安全的”内容,返回给前端。这样是否可⾏呢?
我们举⼀个例⼦,⼀个正常的⽤户输⼊了 5 < 7 这个内容,在写⼊数据库前,被转义,变成了 5 < 7 。
问题是:在提交阶段,我们并不确定内容要输出到哪⾥。
这⾥的“并不确定内容要输出到哪⾥”有两层含义:
在前端中,不同的位置所需的编码也不同。
<div title="comment">5 < 7</div>
所以,输⼊侧过滤能够在某些情况下解决特定的 XSS 问题,但会引⼊很⼤的不确定性和乱码问题。在防范 XSS 攻击时应避免此类⽅法
当然,对于明确的输⼊类型,例如数字、URL、电话号码、邮件地址等等内容,进⾏输⼊过滤还是必要的
既然输⼊过滤并⾮完全可靠,我们就要通过“防⽌浏览器执⾏恶意代码”来防范 XSS。这部分分为两类:
存储型和反射型 XSS 都是在服务端取出恶意代码后,插⼊到响应 HTML ⾥的,攻击者刻意编写的“数据”被内到“代码”中,被浏览器所执⾏。
预防这两种漏洞,有两种常⻅做法:
纯前端渲染的过程:
在纯前端渲染中,我们会明确的告诉浏览器:下⾯要设置的内容是⽂本( .innerText ),还是属性( .setAttribute ),还是样式( .style )等等。浏览器不会被轻易的被欺骗,执⾏预期外的代码了。
但纯前端渲染还需注意避免 DOM 型 XSS 漏洞(例如 onload 事件和 href 中的 javascript:xxx 等,请参考下⽂”预防 DOM 型 XSS 攻击“部分)。
在很多内部、管理系统中,采⽤纯前端渲染是⾮常合适的。但对于性能要求⾼,或有 SEO 需求的⻚⾯,我们仍然要⾯ 对拼接 HTML 的问题。
如果拼接 HTML 是必要的,就需要采⽤合适的转义库,对 HTML 模板各处插⼊点进⾏充分的转义。
常⽤的模板引擎,如 doT.js、ejs、FreeMarker 等,对于 HTML 转义通常只有⼀个规则,就是把 & < > " ' / 这⼏个字符转义掉,确实能起到⼀定的 XSS 防护作⽤,但并不完善:
所以要完善 XSS 防护措施,我们要使⽤更完善更细致的转义策略。
例如 Java ⼯程⾥,常⽤的转义库为 org.owasp.encoder 。以下代码引⽤⾃ org.owasp.encoder 的官⽅说明。
<!-- HTML 标签内⽂字内容 -->
<div><%= Encode.forHtml(UNTRUSTED) %></div>
<!-- HTML 标签属性值 -->
<input value="<%= Encode.forHtml(UNTRUSTED) %>" />
<!-- CSS 属性值 -->
<div style="width:<= Encode.forCssString(UNTRUSTED) %>">
<!-- CSS URL -->
<div style="background:<= Encode.forCssUrl(UNTRUSTED) %>">
<!-- JavaScript 内联代码块 -->
<script>
var msg = "<%= Encode.forJavaScript(UNTRUSTED) %>";
alert(msg);
</script>
<!-- JavaScript 内联代码块内嵌 JSON -->
<script>
var __INITIAL_STATE__ = JSON.parse('<%= Encoder.forJavaScript(data.to_json) %>');
</script>
<!-- HTML 标签内联监听器 -->
<button
onclick="alert('<%= Encode.forJavaScript(UNTRUSTED) %>');">
click me
</button>
<!-- URL 参数 -->
<a href="/search?value=<%= Encode.forUriComponent(UNTRUSTED) %>&order=1#top">
<!-- URL 路径 -->
<a href="/page/<%= Encode.forUriComponent(UNTRUSTED) %>">
<!--
URL.
注意:要根据项⽬情况进⾏过滤,禁⽌掉 "javascript:" 链接、⾮法 scheme 等
-->
<a href='<%=
urlValidator.isValid(UNTRUSTED) ?
Encode.forHtml(UNTRUSTED) :
"/404"
%>'>
link
</a>
可⻅,HTML 的编码是⼗分复杂的,在不同的上下⽂⾥要使⽤相应的转义规则。
DOM 型 XSS 攻击,实际上就是⽹站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执⾏了。
在使⽤ .innerHTML 、 .outerHTML 、 document.write() 时要特别⼩⼼,不要把不可信的数据作为 HTML 插到⻚⾯上,⽽应尽量使⽤ .textContent 、 .setAttribute() 等。
如果⽤ Vue/React 技术栈,并且不使⽤ v-html / dangerouslySetInnerHTML 功能,就在前端 render 阶段避免innerHTML 、 outerHTML 的 XSS 隐患。
DOM 中的内联事件监听器,如 location 、 onclick 、 onerror 、 onload 、 onmouseover 等, 标签的 href 属性,JavaScript 的 eval() 、 setTimeout() 、 setInterval() 等,都能把字符串作为代码运⾏。如果不可信的数据拼接到字符串中传递给这些 API,很容易产⽣安全隐患,请务必避免。
<!-- 内联事件监听器中包含恶意代码 -->
![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2018b/3e724ce0.data:image/png,)
<!-- 链接内包含恶意代码 -->
<a href="UNTRUSTED">1</a>
<script>
// setTimeout()/setInterval() 中调⽤恶意代码
setTimeout("UNTRUSTED")
setInterval("UNTRUSTED")
// location 调⽤恶意代码
location.href = 'UNTRUSTED'
// eval() 中调⽤恶意代码
eval("UNTRUSTED")
</script>
如果项⽬中有⽤到这些的话,⼀定要避免在字符串中拼接不可信数据。
虽然在渲染⻚⾯和执⾏ JavaScript 时,通过谨慎的转义可以防⽌ XSS 的发⽣,但完全依靠开发的谨慎仍然是不够的。
以下介绍⼀些通⽤的⽅案,可以降低 XSS 带来的⻛险和后果。
严格的 CSP 在 XSS 的防范中可以起到以下的作⽤:
对于不受信任的输⼊,都应该限定⼀个合理的⻓度。虽然⽆法完全防⽌ XSS 发⽣,但可以增加 XSS 攻击的难度。
过滤 Html 标签能否防⽌ XSS? 请列举不能的情况?
⽤户除了上传
<script>alert('xss');</script>
还可以使⽤图⽚ url 等⽅式来上传脚本进⾏攻击
<table background="javascript:alert(/xss/)"></table>
<img src="javascript:alert('xss')">
还可以使⽤各种⽅式来回避检查, 例如空格, 回⻋, Tab
<img src="javas cript:
alert('xss')">
还可以通过各种编码转换 (URL 编码, Unicode 编码, HTML 编码, ESCAPE 等) 来绕过检查
<img%20src=%22javascript:alert('xss');%22>
<img src="javascript:alert(/xss/)">
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。