XSS攻击
概念
XSS(Cross Site Scritpting)即跨站脚本攻击,是一种代码注入攻击,为了和CSS区分所以叫XSS。攻击者通过在目标往网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可以获取用户的敏感信息如cookie,sessionID等,进而危害数据安全。
本质
恶意代码未经过过滤,与网站正常代码混在一起,浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。由于直接在用户终端执行,恶意代码能够直接获取用户信息,或者利用这些信息冒充用户向网站发起攻击者定义的请求。同时攻击者可以通过引入外部脚本来完成比较复杂的攻击策略。
XSS注入方法
- 在HTML中内嵌的文本中,恶意内容以script标签形成注入
- 内联javascript中,拼接的数据突破了原本的限制(字符串,变量,方法名等)
- 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签
- 在标签的 href、src等属性中,包含
javascript;等可执行代码 - 在
onload、onerror、onclick等事件中,注入不受控制代码
XSS攻击分类
存储型
恶意代码被保存到目标网站的数据库中,这种攻击具有较强的稳定性和持久性,用户打开目标网站时,网站服务端将恶意代码从数据库中取出,拼接在HTML中返回给浏览器。浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。恶意代码窃取用户数据并发送到攻击者网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。常见的场景比如论坛发帖、商品评论,用户私信等。
反射型
通过引诱用户点击一个链接到目标网站的恶意链接来实施攻击。攻击者构造出特殊的URL,其中包含恶意代码,用户打开带有恶意代码的URL时网站服务器将恶意代码从URL中取出,拼接在HTML中返回给浏览器。用户浏览器接收响应后解析执行,混在其中的恶意代码也被执行。
DOM型
攻击者构造出特殊的URL,其中包含恶意代码。用户打开代有恶意代码的URL,用户浏览器接收到响应后解析执行,前端javascritp取出URL中恶意代码并执行。 DOM型XSS与前两种XSS的区别:DOM型XSS攻击中,取出和执行恶意代码由浏览器端完成,属于前端javascript自身的安全漏洞,而其他两种XSS都属于服务端的安全漏洞。
XSS攻击的防御
XSS攻击主要有两大要素:
1. 攻击者提交恶意代码
2. 浏览器执行恶意代码
预防攻击者提交恶意代码(输入过滤)
-
在用户提交时由前端过滤输入,然后提交到后端
不可行!因为以但攻击者绕过前端过滤直接构造请求,就可以提交恶意代码了 -
后端在写入数据库千对输入进行过滤,然后把“安全的”内容返回给前端。
举个例子:1 < 2首先我们并不确定内容要输出到哪里。- 用户的输入内容可能同时提供给前端和客户端,而一旦经过了
escapeHTML(),客户端显示的内容就会成为乱码(1 < 7) - 在前端,不同位置所需编码也不同。当作为HTML拼接页面时可以正常显示,但是当通过AJAX返回然后赋值给javascript变量时前端得到的就是转义后的字符。 输入过滤能够在某些情况下解决特定的XSS问题,但是会引入不确定性和乱码问题。在防范XSS攻击时要避免采用这种方法。但是对于明确的输入类型,例如:数字、URL、电话号码、邮件地址等内容进行输入过滤还是很有必要的
- 用户的输入内容可能同时提供给前端和客户端,而一旦经过了
预防浏览器执行恶意代码
- 防止HTML中出现注入
- 防止Javascript执行时执行恶意代码
预防存储型和反射型XSS攻击
- 改成纯前端渲染,把代码和数据分隔开(现代前端领域常见的单页应用就是如此)
- 对HTML做充分转义。(开启并利用模板引擎的转移功能,常见的如ejs模板等)
预防DOM型XSS攻击
需要前端自身在书写代码时提高意识。在使用.innerHTML、document.write()时要特别小心,不要把不可信的数据作为html插到页面上,而应尽量使用.textContent、setAtteibute()等。DOM中的内联事件监听器,如location、onclick、onerror、onload、onmouseover等,<a>标签的href属性,javascript的evval()、setTimeout()、setInterval()等都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些API,很容易产生安全隐患。
其他防范措施
- CSP内容安全策略(Content Security Policy)
- 禁止加载外语代码,防止复杂的攻击逻辑
- 禁止外域提交,网站被攻击后用户的数据不会泄露到外域
- 禁止内联脚本执行(规则较严格,目前发现GitHub使用)
- 禁止未授权的脚本执行(新特性,Google Map移动版在使用)
- 合适使用上报可以及时发现XSS,有利于尽快修复问题
- 输入内容长度控制:对于不受信任的输入都应该限定一个合理的长度,这样可以增加XSS攻击的难度。
- Http-only Cookie:禁止 JavaScript 读取某些敏感 Cookie ,攻击者完成XSS攻击后也无法窃取 Cookie。
- 验证码:防止脚本冒充用户体提交危险操作
防范存储型和反身型xss攻击时后端RD的责任,而DOM型XSS攻击不发生在后端是前端RD的责任。转义应该在输出HTML时进行而不是在提交用户输入时进行。 不同的上下文,如:HTML属性,HTML文字内容,HTML注释,跳转链接,内敛JavaScript字符串,内敛CSS样式表,所需要转义的规则不一致。业务RD需要选取合适的转义库,并针对不同的上下文调用不同的转义规则。
参考文章:
tech.meituan.com/2018/09/27/…
segmentfault.com/a/119000000…
CSRF攻击
概念
CSRF(Cross-site request forgery)跨站请求伪造,指黑客诱导用户点击链接打开黑客网站,然后黑客利用用户目前的登录状态发期跨站请求。和XSS攻击对比,CSRF攻击并不需要将恶意代码注入用户当前页面的HTML文档中,而是跳转到新的页面,利用服务器的验证漏洞和用户之前的登录状态来模拟用户进行操作。
攻击原理
点击链接之后
-
自动发 GET 请求 黑客网站中可能有这样一段代码
<img src="https://xxx.com/info?user=hhh&count=100">进入页面后会自动发起一个get请求,这个请求会自动带上关于xxx.com的cookie信息(如果你已经在xxx.com中登陆过)
假如服务器端没有相应的验证机制,它可能认为发请求的是一个正常的用户,因为携带了相应的cookie,然后进行相应的各种操作,可以是转账汇款以及其他的恶意操作。 -
自动发起 POST 请求 黑客可能自己填了一个表单,写了一段自动提交的脚本。
form id='hacker-form' action="https://xxx.com/info" method="POST">
<input type="hidden" name="user" value="hhh" />
<input type="hidden" name="count" value="100" />
</form>
<script>document.getElementById('hacker-form').submit();</script>
同样也会携带相应的用户 Cookie信息,让服务器误以为是一个正常的用户在操作,让各种恶意操作变为可能。
- 诱导点击发送 GET 请求
在黑客网站上可能会放一个链接,驱使你来点击
<a href="https://xxx/info?user=hhh&count=100" taget="_blank">点击进入修仙世界</a>点击时候和自动发起 get 请求部分同理
防范措施
-
利用 Cookie 的 Samesite 属性
CSRF攻击中重要的一个环节就是自动发送目标站点下 Cookie,然后就是这一份 Cookie 模拟了用户的身份,故在 cookie 上面下文章是防范的不二人选。 在 cookie 中有一个关键的字段Samesite可以对请求中的 cookie 的携带做一些限制。Strict: 浏览器完全禁止第三方请求携带 cookie,比如请求sanyuan.com网站只能在sanyuan.com域名中的请求才能携带 cookie,其他网站请求都不能携带 cookie.
Lax:只能在 get 方法提交表单或 a 标签发送 get 请求下可以携带 cookie
None:即默认模式,请求会自动携带上 cookie -
验证来源站点
利用请求头中的两个字段:Origin和Referer
Origin只包含域名信息,Referer包含具体的URL,但是这两者也都可以伪造,通过AJAX请求头即可,安全性略差 -
CSRF Token
首先浏览器向服务器发送请求,服务器生成一个字符串,将其植入到返回的页面中。然后浏览器如果要发送请求就必须带上这个字符串,然后服务器来验证是否合法,如果不合法就不响应,这个字符串就是 CSRF Token,通常第三方无法拿到这个token因此会被服务器给拒绝。在浏览器访问网站A时,网站A设置cookie会增加随机值csrf_token(也就是token)。
对cookie不是太了解的同学请注意:CSRF是网站B通过盗用保存在客户端的cookie,以客户端的身份来非法访问网站A。而虽然token也是由后端放在cookie中的,但是需要前端在发送请求的时候来将这个token发给后端检验,从而达到防御的目的。
Token就是令牌,最大的特点就是随机性,不可预测。
返回给浏览器时,cookie会储存在浏览器。然后前端提交表单或者非表单请求的时候,就将这个token获取到然后发送给后端,后端判断请求中的token和cookie中的token是否一致来判断是否为正常请求,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
为什么放在cookie之后,别的网站获取不到cookie里面的token而自己的网站可以获取token中的cookie呢?
因为cookie采取同源策略,只有相同域名的网页才能获取域名对应的cookie。
表单请求<form method="post"> <input type="hidden" name="csrf_token" value="{{ csrf_token }}"> <label>账户:</label><input type="text" name="to_account" placeholder="请输入对方账户"><br/> <label>金额:</label><input type="number" name="money" placeholder="请输入转账金额"><br/> <input type="submit" value="转账"> </form>非表单请求: 在ajax获取数据时,添加 headers:{ 'X-CSRFToken':getCookie('csrf_token') }
import axios from 'axios' axios.post('/user', { name: 'Lumiere' }, { headers:{ 'X-CSRFToken':getCookie('csrf_token') } }) .then(function (response) { console.log(response) }) .catch(function (error) { console.log(error) })但token也非绝对安全,因为就算token藏的再隐蔽,但是这一切都会暴露在前端,所以黑客可根据网站前端代码进行分析,然后写一套脚本自动化抓取token,然后对服务端接口实施攻击。
-
验证码
验证码是一种非常强大的防范CSRF的方式,例如各种图形验证码(12306的高难度图形验证),可以达到有效的防御功能。
但是不可能所有接口都是用验证码,这样用户体验会很差,所以 比较好的方式是尽量减少图形验证码的使用,对于一些操作不敏感的接口使用token+referrer来防御。
但是图形也不是绝对安全,因为目前图像识别已经很先进,简单的还是可以破解的...但是为了安全,还是需要进行各种防御的设置。