开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第16天,点击查看活动详情
一、简介
跨站脚本攻击,英文全称是Cross Site Script,本来缩写完成时CSS,但为了和层叠样式表(Cascading Style Sheet,CSS)区分开来,所以在网络安全中叫做XSS。
XSS是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。
XSS的本质: 恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。 而由于直接在用户的终端执行,恶意代码能够直接获取用户的信息,或者利用这些信息冒充用户向网站发起攻击者定义的请求。
1.2 如何攻击
我们在这简单的演示一个例子,来描述下是如何进行XSS攻击的
data: {
content: '<img src="xxx" onerror="alert(123)">'
}
<div v-html="content">
</div>
1.3 危害性
- 窃取Cookie
- 按键记录或者钓鱼
- 利用虚假输入表单骗取用户个人信息
- 利用脚本窃取用户的 Cookie,被害者在不知情的情况下,帮助攻击者发送恶意请求
- 显示伪造的文章或图片
- 偷取网站任意数据
- 偷取用户资料
- 偷取用户密码和登录状态
- 欺骗用户
- 未授权操作
- 获取页面数据
- 获取 Cookies
- 获取本地存储数据
- 劫持前端逻辑
- 发送请求
- 图片、表单等
- ...危害还有很多
二 XSS分类
- 反射型 XSS:url
- 存储型 XSS:数据持久化
- DOM XSS:纯前端 DOM 操作
反射型和存储型在纯前端渲染中已经很少见了,尤其是在大量使用第三方框架的前提下。
例如:Vue,React,Angular,...
2.1 反射型 XSS
黑客往往诱导用户点击一个恶意链接,才能攻击成功。
攻击步骤:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
场景: 通过 URL 传递参数的功能,如网站搜索、跳转等。由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。
请看如下示例:
<h1 class="logo-font" v-html="$route.query.content"></h1>
请求方式:
http://localhost:8080/#/?content=<img src="xxx" onerror="alert(123)">
上面只是说明一些攻击方式,在自己实现过程中,需要搭建自己的服务端进行验证。例如,input输入框中输入一段脚本,后端将此数据返回给前端,这种情况,就形成了攻击,详细代码请戳
总结
如果开发者没有将用户输入的文本进行合适的过滤,就贸然插入到 HTML 中,这很容易造成注入漏洞。攻击者可以利用漏洞,构造出恶意的代码指令,进而利用恶意代码危害数据安全。
- 在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入。
- 在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)。
- 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签。
- 在标签的 href、src 等属性中,包含 javascript: 等可执行代码。
- 在 onload、onerror、onclick 等事件中,注入不受控制代码。
- 在 style 属性和标签中,包含类似 background-image:url("javascript:..."); 的代码(新版本浏览器已经可以防范)。
- 在 style 属性和标签中,包含类似 expression(...) 的 CSS 表达式代码(新版本浏览器已经可以防范)。
2.2 存储型XSS
存储型会把用户输入的数据“存储”在服务器端。
存储型 XSS 的攻击步骤:
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
请看如下场景:
- 内容一般使用富文本编辑器,富文本编辑器最终产生的结果:HTML 格式的字符串
- Web 后台就会把文章内容存储到数据库中,用于文章详情展示
- 当有用户查看文章详情的时候,就会渲染文章内容
- 若用户提交的数据中有script标签或者注入其他信息,未被处理的情况下保存到的数据库中。则查询时会出现XSS攻击
2.3 DOM XSS
通过修改页面的DOM节点形成的XSS,称之为DOM XSS,严格意义来说,效果和反射型XSS类似,之所以单独划分,是因为DOM XSS形成原因比较特别。
DOM 型 XSS 的攻击步骤:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL。
- 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
注意:DOM 型 XSS 攻击中属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
三、XSS攻击防御
XSS防御是复杂的,流行的浏览器都内置了一些XSS防御措施,例如Firefox的CSP,Noscript扩展,IE8内置的XSS Filter等。
3.1 常用方法
3.1.1 使用HttpOnly
浏览器将禁止页面的Javascript访问带有HttpOnly属性的Cookie。
严格意义来说,HttpOnly并未为了对抗XSS,它解决的是XSS后的Cookie劫持攻击。
总结来说,HttpOnly有助于缓解XSS攻击,但仍然需要其他能够解决XSS漏洞的方案。
3.1.2 输入过滤
常见的漏洞如XSS,SQL Injection等,都需要攻击者构造一些特殊字符,这些特殊字符一般正常用户是不会用到的,所以输入过滤就显得尤为重要。
问题来了?输入过滤,需要在前端检查还是后端呢。
-
大部分人的想法是在前端检查,但是我们想一下,如果用户直接构造请求,那么就可以发送恶意代码了。
-
在后端存入数据库前进行过滤检查,然后将过滤后的信息返回给前端。
- 若
a>b,转移过滤后是a>b - 后端存储数据库后,下次调用返回
a>b - 若直接在html页面显示,则显示为a>b;
- 返回后,这个内容不能直接用于 Vue 等模板的展示,也不能直接用于内容长度计算,不能用于标题、alert 等。而且使用在android,ios,等系统上,是无法正常显示的。
- 若
总结: 输入侧过滤能够在某些情况下解决特定的 XSS 问题,但会引入很大的不确定性和乱码问题。在防范 XSS 攻击时应避免此类方法。
当然,对于明确的输入类型,例如数字、URL、电话号码、邮件地址等等内容,进行输入过滤还是必要的。
既然输入过滤并非完全可靠,我们就要通过“防止浏览器执行恶意代码”来防范 XSS。这部分分为两类:
- 防止 HTML 中出现注入。
- 防止 JavaScript 执行时,执行恶意代码。
结论:不建议在输入的时候对数据进行转义,而是在输出的时候。
3.1.3 转义 HTML
function escape(str) {
str = str.replace(/&/g, '&')
str = str.replace(/</g, '<')
str = str.replace(/>/g, '>')
str = str.replace(/"/g, '&quto;')
str = str.replace(/'/g, ''')
str = str.replace(/`/g, '`')
str = str.replace(///g, '/')
return str
}
一般是不会对空格进行转移的,而且日常开发过程中,可以使用jsxss库进行转义
3.1.4 配置白名单
对于富文来说,我们是没办法通过转义来防止攻击的。
配置白名单,比如,只允许<a><div>等比较安全的标签存在。
富文本中用户自定义的一些CSS,style也会导致XSS攻击。因此要尽可能的禁止用户自定义CSS与style。如果一定要自定义,则需要过滤CSS,对CSS进行智能解析,检测其中危险代码。
实际开发过程中,可以使用jsxss对富文本进行解析,也可以使用cheerio
3.2 预防存储型和反射型 XSS 攻击
前面讲到,存储型和反射型 XSS 都是在服务端取出恶意代码后,插入到响应 HTML 里的,攻击者刻意编写的“数据”被内嵌到“代码”中,被浏览器所执行。
预防这两种漏洞,我们常见的做法:
- 改成纯前端渲染,把代码和数据分隔开。
- 对 HTML 做充分转义。
转义这部分我们在之前已经详细的介绍过了,在这里我们就不做过度的赘述。
纯前端渲染
过程:
- 浏览器先加载一个静态 HTML,此 HTML 中不包含任何跟业务相关的数据。
- 然后浏览器执行 HTML 中的 JavaScript。
- JavaScript 通过 Ajax 加载业务数据,调用 DOM API 更新到页面上。
在纯前端渲染中,我们会明确的告诉浏览器:下面要设置的内容是文本(.innerText),还是属性(.setAttribute),还是样式(.style)等等。浏览器不会被轻易的被欺骗,执行预期外的代码了。
3.3 预防 DOM XSS 攻击
DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。
例如:
<div id="t"></div>
<input type="text" id="text" value="" />
<input type="button" id="s" value="write" onclick="test()" />
<script>
function test() {
var str = document.getElementById("text").value;
document.getElementById("t").innerHTML =
"<a href='" + str + "'>testLink</a>";
}
</script>
会触发DOM 型XSS的地方有很多:
document.write()document.writeln()xxx.innerHTML =xxx.outerHTML=innerHTML.replacedocument.location.replace()document.location.assign()- ...
除了上述直接操作,我们还需要重点关注
eval()setTimeout()setInterval()- 页面中的input框
- DOM中的事件监听器:
location、onclick、onerror、onload、onmouseover等 - a标签中的href属性
3.4 XSS 的检测
-
使用通用 XSS 攻击字串手动检测 XSS 漏洞
jaVasCript:/*-/*`/*`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e能够检测到存在于 HTML 属性、HTML 文字内容、HTML 注释、跳转链接、内联 JavaScript 字符串、内联 CSS 样式表等多种上下文中的 XSS 漏洞,也能检测 eval()、setTimeout()、setInterval()、Function()、innerHTML、document.write() 等 DOM 型 XSS 漏洞,并且能绕过一些 XSS 过滤器。
-
使用扫描工具
- Arachni:Arachni是基于Ruby的开源,功能全面,高性能的漏洞扫描框架
- Mozilla HTTP Observatory:Mozilla最近发布的一款名为Observatory的网站安全分析工具
- w3af:W3af是一个基于Python的Web应用安全扫描器
总结
XSS漏洞虽然复杂,但确可以彻底解决,在设计XSS解决方案的时候,应该深入理解XSS攻击原理,针对不同的场景使用不同的方法。