这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战
前言
跨站脚本攻击(Cross-site scripting)是一种常见的web漏洞,英文缩写为了不与层叠样式表CSS冲突便称为XSS。
XSS攻击的原理是攻击者往页面里插入恶意脚本代码,当用户浏览网页时,嵌入到其中恶意代码便被执行,从而达到盗取用户信息或其他侵犯用户安全隐私的目的。
虽然XSS的攻击方式千变万化,但可以大致分为3类:持久型、反射型、DOM 型。
持久型
持久型XSS又称存储型XSS,顾名思义,XSS攻击代码被存储在服务端数据库中,当服务器收到相应请求时,XSS攻击代码被嵌入到资源文件中作为响应发送给客户端,从而发动攻击。
以喜马拉雅FM曾经的一次XSS漏洞为例,在编辑专辑名称时,没有对专辑名进行转义过滤就保存到服务器,如果保存的专辑名是一段以<script>...</script>包裹的恶意脚本,那么当用户打开相关网页时,恶意代码作为页面源代码的一部分被浏览器解析执行,攻击达成。
反射型
反射型XSS又称非持久型 XSS,当用户点击一个恶意链接、提交一个表单或者进入一个恶意网站时,恶意代码便会和正常返回数据一起作为响应发送到受害者的浏览器,从而骗过了浏览器,使之误以为恶意脚本来自于可信的服务器,以至于让恶意脚本得以执行。
原理
攻击者诱导用户访问一个带有恶意代码的 URL 后,服务器端接收数据后处理,然后把带有恶意代码的数据发送到浏览器端,浏览器端解析这段带有 XSS 代码的数据后当做脚本执行,最终完成 XSS 攻击,这个过程就像一次反射,故称为 反射型 XSS 。
攻击步骤
- 攻击者构造出特殊的
URL,其中包含恶意代码。 - 用户被诱导打开带有恶意代码的
URL,服务器端将恶意代码从URL中取出当做参数处理,然后返回给用户带有恶意代码的数据。 - 用户浏览器接收到响应解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户敏感数据发送给攻击者,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM型
基于DOM型的XSS攻击完全在浏览器上进行,与服务器无关,当页面需要使用用户输入来创建DOM元素时,就有可能受到XSS攻击。
以当前编辑器为例,当我们使用![]()语法插入图片时,编辑器会使用我们输入的图片URL来创建一个img元素,这时,我们可以通过这个特性来发起DOM型XSS攻击。
首先构建一段markdown攻击代码:
)
#"onerror="alert('这是一次xss攻击')会被编辑器作为图片URL,插入到img的src属性中,由于html中属性由一对双引号包裹,因此构建出来的img标签是这样的:
<img src="#" onerror="alert('这是一次xss攻击')" alt="img">
理论上,这时会弹出一个警示栏,如下所示:
然而,由于编辑器会对URL进行了转义,将双引号、尖括号等字符转义成了16进制码值,攻击无效。由此看出,对用户输入进行转义处理是防御XSS攻击的重要手段。
上面的攻击代码只是破坏了页面结构,我们还可以通过拼接URL创建一个script标签来发动更高级的攻击来窃取cookies等,并且由于编辑器是在线保存的,攻击代码还会被存储到服务器,如果服务器没有防御措施,一旦文章发布,所有浏览文章的用户都会受到攻击,这就变成了持久型XSS。
防御
为了防御XSS攻击,需要前后端共同努力:
- 前端在渲染页面
DOM的时候应该选择不相信任何后端数据,任何字段都需要做转义处理。 - 前端对于需要即时转换成
DOM的用户输入,要进行转义处理,尤其是富文本的处理。 - 后端在入库前应该选择不相信任何前端数据,将所有的字段统一进行转义处理。
- 后端在输出给前端数据统一进行转义处理。
- 严格管理
cookie的读写权限,例如设置cookie的http-only属性。
总结
对于前端,用户的输入和是永远不可信任的,对于后端,前端的输入也是永远不可信任的,否则,攻击者可以利用这个漏洞在网站上注入恶意代码,从而窃取用户的一些权限信息(cookie,session tokens)或者对网站结构进行破坏。通过前后端双重验证,可以防护绝大部分XSS攻击。