简单分析XSS与CSRF
浏览器安全策略
同源策略
浏览器的同源策略限制了来自不同源的 document 或 脚本,对当前文档读取或者设置某些属性。
同源:协议、域名、端口三者都相同,则算同源
同源策略是浏览器为了保证资源的安全性和私密性,只有浏览器存在同源策略。
同源策略限制的元素:
- Cookie、LocalStorage、IndexDB等存储性内容
- DOM
- XHR
为什么<script>
、<img>
、<iframe>
、<link>
这些标签可以跨域请求资源?
- 这些带 src 属性的标签每次加载时,实际上是由浏览器发起了一次GET请求
- 通过 src属性加载的资源,浏览器限制了JavaScript的权限,使其不能读写返回的内容
跨域常见手段
window.domain + iframe
大法用于不同子域的iframe通信- 该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式。
- 只需要给页面添加
document.domain ='test.com'
表示二级域名都相同就可以实现跨域。(两个页面都通过js强制设置document.domain为基础主域,就实现了同域。)
location.hash + iframe
:通过修改location.hash
值,对应页面检测到hashchange做出响应来实现双向通信- 父窗口可以对iframe进行URL读写,iframe也可以读写父窗口的URL
- IE、chrome下,若跨域 无法直接修改parent.localtion.hash:在iframe页面嵌入一个与父页面同域的iframe
HTML5提供的postMessage:
- 解决的问题:
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的iframe消息传递
- 实现方案:
- A页面:
otherWindow.postMessage(message, targetOrigin)
- B页面:监听message事件
- A页面:
- 解决的问题:
window.name + iframe:
- window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
jsonp跨域
利用
<script>
标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的JSON数据。JSONP请求一定需要对方的服务器支持才可以。前端页面声明一个回调函数,通过script标签向API发起请求并带上查询字符串
callback=函数名
服务器收到请求后,解析得到函数名(例如show),返回
show(要传输的数据字符串)
给浏览器浏览器得到响应,客户端就会调用之前声明的回调函数
实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function jsonp({url, params, callback}) {
return new Promise((resolve, reject) => {
let scrpt = document.createElement('script')
window[callback] = function(data) {
resolve(data)
document.body.removeChild(script)
}
params = {...params, callback}
let arrs = []
for (const key in params) {
arrs.push(`${key}=${params[key]}`)
}
script.src = `${url}?${arrs.join('&')}`
document.body.appendChild(scrip)
})
}
Websocket
- WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。
- 同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。
Nginx反向代理
- 通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机
- 反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录
Node中间层:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。
- 接受客户端请求 。
- 将请求转发给服务器。
- 拿到服务器响应数据。
- 将响应转发给客户端。
CORS
CORS需要浏览器和后端同时支持,IE8和IE9需要通过XDomainRequest来实现
服务端设置Access-Control-Allow-Origin就可以开启CORS,该属性表示哪些域名可以访问资源。
简单请求,同时满足下列2个条件就属于简单请求
- 使用下列方法之一:GET、HEAD、POST
- Content-Type的值局限于下列三者之一:text/plain、multipart/form-data、application/x-www-form-urlencoded
复杂请求:不是简单请求的就是复杂请求
- 复杂请求的CORS请求,会在正式通信前,增加一次HTTP查询请求,称为“预检”请求,方法为Option,通过该请求来知道服务端是否允许跨域请求。
浏览器沙箱 sandbox
Chrome、IE都是多进程架构,每个tab是一个进程,进程之间资源互相隔离。
恶意网站拦截
通过设置黑名单,防止用户访问或请求黑名单上的网站或资源。
CSP:Content Security Policy
- 禁止加载外域代码,防止复杂的攻击逻辑
- 禁止外域提交,网站被攻击后,用户的数据不会泄露到外域
- 禁止内联脚本执行
- 禁止未授权的脚本执行
- 合理使用上报可以及时发现 XSS,利于尽快修复问题
XSS
XSS(Cross Site Script,跨站脚本攻击),通过 HTML注入篡改网页(插入恶意脚本)达到攻击的方式。
本质:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。
实现
XSS分类:
- 反射型XSS:恶意代码存储在url中,需要与用户交互才能触发
- 攻击者构造出特殊的url,其中包含恶意代码
- 用户打开带有恶意代码的url时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器
- 浏览器接收到响应后解析执行,恶意脚本也得到执行
- 存储型XSS:将用户包含恶意脚本的输入存储到服务端
- 攻击者将恶意代码存储到网站服务器【数据库】
- 用户访问网站时,浏览器从服务器拉取恶意代码拼接在HTML上
- 浏览器接收到响应后解析执行,恶意脚本也得到执行
- DOM Based XSS:通过 修改页面的DOM节点 形成的XSS
- 例:【待补充】
危害
- XSS Payload:
- Cookie劫持:通过植入的恶意脚本读取浏览器的cookie对象
document.cookie
- 解决方法一:在服务端配置Cookie【
HttpOnly
】,脚本无法直接读取Cookie - 解决方法二:将Cookie与IP绑定,这样即使恶意脚本窃取了Cookie,其他用户也无法用此cookie发生事务(除非伪造IP)
- 解决方法一:在服务端配置Cookie【
- 构造GET/POST请求
- 构造GET请求比较容易,借助天然跨域的
<img>
即可,将请求地址设置为图片的src。 - 对于POST请求,要麻烦一些(需要插入下面的恶意代码,然后发起请求)
- 构造form表单,提交
- 通过XHR发起POST请求
- 构造GET请求比较容易,借助天然跨域的
- Cookie劫持:通过植入的恶意脚本读取浏览器的cookie对象
- XSS 钓鱼: 通过XSS脚本伪造页面欺骗用户
- 识别用户系统层面的信息,例如浏览器版本、安装的软件、IP地址等
预防
XSS本质就是JavaScript脚本注入,所以可通过检查输入输出来预防
- 输入检查
- 结合XSS特性对字符串进行转译【例如:转译
<script>
、<base>
等】 、 XSS filter - 但可能会破坏上下文语境,只针对部分有用,不推荐
- 结合XSS特性对字符串进行转译【例如:转译
- 输出过滤:存储型XSS和反射型XSS都是将恶意代码拼接到HTML上
- 纯前端渲染:前后端分离,数据与表现分离,所以能很好的避免XSS(SEO解决?DOM型XSS也无法避免)
- 对HTML进行转义
- 白名单: 处理富文本
- 预防DOM 型 XSS 攻击
- 谨慎使用可将数据插入到文档中,谨防插入恶意代码
CSRF
CSRF(跨站点请求伪造):利用用户的登录态发起恶意请求
原理:
- 利用用户在被攻击网站的登录态,使得在进行跨站请求是通过了服务端的验证
- 浏览器允许发送第三方cookie【广告精准投放的基础】
实现
- Session-Cookie
- 未设置cookie过期时间,cookie存储在内存中
- 由于一个tab拥有一块内存,所以 同一tab下即使不同域 实际上共享Session-Cookie
- 【Third Party COokie,设置了过期时间,存储在本地,在 同一域下所有tab共享】
- P3P(The Platform for Privacy Preferences)
- 如果HTTP设置了P3P头,那么允许浏览器发送第三方cookie
预防
- CSRF发生在第三方域名
- 同源检测
- Origin:若Origin存在可通过Origin来确认域。
- 对于IE:
- IE不会在跨站CORS请求头上加上Origin标头
- 在302重定向后,Orgin不包含在重定向的请求中
- 对于IE:
- Referrer
- 使用方法:HTTP头、
<meta>
标签、referrerpolicy属性 - Referrer-Policy值:
- no-referrer【不发送】
- no-referrer-when-downgrade【HTTPS—> HTTP,不发送】
- same-origin【同源时发送】
- origin【只发送源信息】
- strict-origin【HTTPS—>HTTP不发送,其余同origin】
- origin-when-cross-origin【同源时发送完整字段,跨域时同origin】
- strict-origin-when-cross-origin【同源发送,跨域&降级不发送,跨域只发送origin】
- unsafe-url【字段包含源信息、路径和查询字符串,不包含锚点、用户名和密码】
- 使用方法:HTTP头、
- Origin:若Origin存在可通过Origin来确认域。
- Samesite Cookie【用于限制第三方Cookie,同时子域也被限制。。。。】
- Strict:禁止第三方Cookie
- Lax:GET请求ok
- None
- 同源检测
- CSRF不能获取到Cookie等信息,只是可以使用
- Anti CSRF Token
- 服务端配置token,将token输出到页面中
- 页面提交请求的时候携带token
- 服务器验证token是否有效
- 双重Cookie验证
- 由于CSRF通常无法获取到cookie,我们可以将cookie中的某key-value添加到请求url参数上,后端收到请求后进行验证
- 缺点:任何跨域都无法获取到cookie【包括子域名】、不能对用于双重验证的cookie设置http-only【存在XSS修改cookie风险】
- Anti CSRF Token
- 验证码:强制用户在发送请求时验证
- 保持页面的幂等性,GET操作不要做用户操作
总结
在日常业务开发中,推荐按照《白帽子讲Web安全》中提出的安全方案设计法则来去应对潜在的安全问题。
- Secure By Default: 黑白名单、最小权限原则
- 纵深防御原则
- 数据与代码分离原则
- 不可预测性原则