最近公司的新版本开发,由于后端代码的更迭,前端出现了不少的xss漏洞,这里总结一下关于xss漏洞和前端转义的知识。
xss攻击,即跨站脚本攻击,是前端所遇到的最普遍的网络攻击,也是前端最常见并需要防护的攻击。
记得在今年年初,腾讯邮箱就出过一次xss漏洞,据说有开发者反馈到腾讯,才被发现解决,不过已经有黑客利用这个漏洞,到处发带有xss攻击的邮件,盗取了大量账号。可见,xss虽然简单,但攻击防不胜防,即使是腾讯这样的大公司也可能出现疏漏。而且一旦漏洞被黑客利用,直接就能通过获取cookie等手段盗取用户账号,引起重大损失。
关于XSS
这里先介绍一下xss攻击的简单实现。通常前端总是免不了和数据打交道,经常要插入数据进dom中,这就给了攻击者可趁之机,因为浏览器并不知道你是想要单纯的插入dom,还是插入脚本,一旦恶意脚本被注入,攻击者就能简单地获取到用户的信息。
这段代码中,一担’xss攻击’字符串变为<img src=1 onerror=alert("xss")>
,就会将脚本注入进去。这只是众多xss攻击中的其中一种,有兴趣的可以自行搜索。通过恶意代码的注入,攻击者就能够从document.cookie
中取得用户cookie然后通过ajax发送出去。
关于htmlEncode
和 htmlDecode
html中是不允许某些特殊字符出现的,比如说<>
,一旦出现这种字符,浏览器将会把他当成一个闭合标签,而不是单纯的文本,引起解析错误,因此我们需要对特殊字符进行encode(转义),而浏览器在输出这些转义后的字符时,会自行进行一次decode(反转义),比如往浏览器输入<
将会被decode成<
,而被decode后的字符,就只是单纯的字符串,而不会被浏览器解析。
如何防止XSS攻击
插入到dom的文本中出现特殊字符,都是及其危险的事,而攻击者则会想方设法让代码中出现特殊字符。通常,对XSS攻击的防御要从前后端两方面来做。只要有用户输入的地方,都要进行encode,比如用户名,简介,地址等等,防止前端数据中直接出现<>等特殊字符,入口侧从后端来阻止,出口侧则由前端来阻止。基本上对于现代浏览器,只需要保证对尖括号进行了处理,就能阻止各类神奇的xss攻击。
通常前端的encode方法有两种。
方案一
通过create一个文本节点来插入div,再取出来,来达成encode,这种方法的优点就是利用源生的方法,转义快速且全面。
还有一种方案就是利用正则来替换特殊字符串来实现,一般的模板引擎或者框架都会进行encode来保护代码安全,下面我们来看一下doT.js模板源码encode的实现
其实就是对固定的一套特殊字符进行替换,注意这里的doNotSkipEncoded
,在有些版本是没有,默认为false
的,这个就是为了防止二次转义。
要知道特殊字符大多转成'&'
或者'<'
的形式,而&
本身就是特殊字符,还可以继续进行转义,所以htmlEncode(htmlEncode('<'))
则会变成'&lt;'
,将这段字符插入dom中,将会得到'<'
,对用户来说自己只是输了一个’<’就变成了乱码,所以/&(?!#?\w+;)|<|>|"|'|\//g
这段正则中默认排除了&(?!#?\w+;)
这种情况进行二次转义。
这段方法可以简化成
function htmlEncodeReg (str) {
var encodeHTMLRules = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/'
};
var matchHTML = /&(?!#?\w+;)|<|>|"|'|\//g;
return str ? str.toString().replace(matchHTML, function (m) { return encodeHTMLRules[m] || m; }) : '';
}
两种方法互有优缺点,前一种方法容易引发的会提是后端转义一次+前端也转义一次,引发二次转义,用户输入的<>&
等字符会变成<>'
,而后一种方法引发问题的情况是后端没转义,用户输入<>'
等代码会变成<>&
。
总结
在实际生产环境中,如果不考虑某些数据库自己的问题,后端在数据输入口进行处理其实是最为妥当的,再不济也可以在每个接口进行转义,因为前端的数据出口随需求增长很快,很容易出现纰漏。
但是每个公司都会有自己的历史遗留,作为一个前端,你必须想办法把所有的数据出口都进行保护,但又不能保证后端处理的正确一致。会出现有些数据encode了,而有些没有,甚至后端以前为了防止sql注入攻击,仅仅把所有引号都转义了,后来随着新工具的出现,又开始不转的情况。
如果前端清一色的转义,很可能出现二次转义,而发生乱码问题。
经过讨论,我们结合自己实际情况,决定前端统一使用正则替换的方法encode,毕竟用户输入转义后的字符几率较小,不至于出现重大bug,而后端尽量把所有接口都进行一次encode,这样两方配合,能最大的减少出现XSS漏洞的情况。