postMessage

postMessage常用于当前页面与iframe内页面,传递跨域信息

如果对发送端或接受端未做校检,就会存在这样那样的问题

发送端未指定target

下面为页面aaa接受来自窗口bbb的信息

1
2
3
4
5
6
7
8
9
10
11
12
<!-- http://www.aaa.com:1234/receive.html -->
<p id="text"></p>
<iframe id="target" src="http://www.bbb.com:1234/send.html"></iframe>
<script>
window.addEventListener('message', receiveMessage, false);
function receiveMessage(event) {
if(event.origin != 'http://www.bbb.com:1234') {
return;
}
document.getElementById("text").innerHTML = event.data;
}
</script>
1
2
3
4
5
<!-- http://www.bbb.com:1234/send.html -->
<script>
//向父窗口发送用户的SercretKey,指定域为*
window.parent.postMessage("SercretKey", "*");
</script>

正常状态如下:

但是上面发送端代码中指定target为*意味着任意域都可以接收这一信息

在恶意网站ccc上布置接收代码,用户访问后,就可以拿到用户的SercretKey

接收端未校检origin

下面为aaa向窗口bbb传递信息SecretKey,bbb接收到后将内容显示到页面里

1
2
3
4
5
6
<!-- http://www.aaa.com:1234/send.html -->
<iframe id="target" src="http://www.bbb.com:1234/receive.html"></iframe>
<script>
var target = document.getElementById("target");
target.contentWindow.postMessage("SecretKey", "http://www.bbb.com:1234");
</script>
1
2
3
4
5
6
7
8
9
10
<!-- http://www.bbb.com:1234/receive.html -->

<p id="text"></p>
<script>
// 监听postMessage,并使用receiveMessage函数处理
window.addEventListener('message', receiveMessage, false);
function receiveMessage(event) {
document.getElementById("text").innerHTML = event.data;
}
</script>

正常状态如下:

但是在bbb中的接收代码中,没有对发送信息的来源进行校检

我们可以搭建一个恶意网站,伪造信息发送过去

1
2
3
4
5
6
<!-- http://www.evil.com:8888/evil.html -->
<iframe id="target" src="http://www.bbb.com:1234/receive.html"></iframe>
<script>
var target = document.getElementById("target");
target.contentWindow.postMessage("<svg/onload=alert(xss)>", "http://www.bbb.com:1234");
</script>

上面的代码其实就是正常往目标传递信息,但是发送内容变成了XSS代码,用户访问我们的恶意界面后

防御方案

发送端指定target

1
2
3
<script>
window.parent.postMessage("SercretKey", "http://www.bbb.com:1234");
</script>

接收端对origin进行校检

1
2
3
4
5
6
7
8
9
10
<p id="text"></p>
<script>
window.addEventListener('message', receiveMessage, false);
function receiveMessage(event) {
if('http://www.aaa.com:1234' != event.origin) {
return;
}
document.getElementById("text").innerHTML = event.data;
}
</script>

JSONP劫持

jsonp不像postMessage那样需要用iframe来进行跨域数据传输,而是活用script标签进行跨域数据请求

1
2
3
4
5
6
# http://www.bbb.com:1234/test.php
<?php
//没有声明返回包Content-type,默认为html,会存在XSS的问题
header("Content-type: text/json");
$callback = $_GET['callback'];
echo $callback.'({"id":1, "name":"x1a0t"})';
1
2
3
4
5
6
7
8
<!-- http://www.aaa.com:1234/index.html -->
<script>
function jsonp(message) {
// 对取到的数据进行操作
alert(message);
}
</script>
<script src="http://www.bbb.com:1234/test.php?callback=jsonp"></script>

index.html中,第一个script标签先声明了一个函数jsonp(message),后面的script标签跨域获取数据作为js代码执行,等于执行了函数jsonp({"id":1, "name":"x1a0t"}),于是我们将另一个站的数据成功引进入到了当前站的网页中

但是可以看到test.php代码中并没有对请求者进行校检,攻击人员可以构造恶意界面让用户访问,即可窃取用户敏感信息

防御方案

jsonp劫持属于CSRF范畴,可添加token校检代码

以及test.php中的代码需要做好XSS防御,即限制返回包Content-type不要为html

CORS

jsonp是通过script标签GET请求获取的信息,如果需要用到POST等方法请求数据,就需要用CORS(跨域资源请求)

我们的请求包中如果有Origin头,就会被判定为CORS请求,服务端会在返回包中以Access为前缀的头就会起作用,关键的有两个

  • Access-Control-Allow-Origin,必须,允许的Origin
  • Access-Control-Allow-Credentials,可选,允许发送cookies等凭证

举例,某线上业务中,前端页面aaa通过某个后端api(bbb),根据用户cookie等凭证取出用户信息,返回到当前页面中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- http://www.aaa.com:1234/index.html -->
<script>
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.readyState == 4) {
// 对取到的数据进行操作
alert(xhr.responseText);
}
}
//允许发送浏览器发送请求目标的cookie等凭证
xhr.withCredentials = true;
xhr.open("POST", "http://www.bbb.com:1234/test.php");
xhr.send();
</script>
1
2
3
4
5
6
7
8
9
10
<?php
# http://www.bbb.com:1234/test.php
# 重要配置
header("Access-Control-Allow-Origin: http://www.aaa.com:1234");
header("Access-Control-Allow-Credentials: true");

# 模仿认证过程,取出敏感信息
if($_COOKIE['user']) {
echo $_COOKIE['user']."'s SecretKey";
}

用户有bbb需要的凭证,然后访问aaa页面,aaa发起CORS请求,bbb验证凭证后取出信息返回,浏览器判断返回包是否允许当前域,是否允许凭证,都允许则取出数据,正常情况如下

如果CORS出现错误配置Access-Control-Allow-Origin: *,即允许所有的Origin,就会造成CORS攻击

攻击者可以构建恶意网页让用户访问,窃取用户敏感信息

防御方案

通过Access-Control-Allow-Origin指定允许特定的origin

CSWH

WebSocket拥有支持双向通信,实时性较强,还支持二进制通信等优点

如果WebSocket服务器没有限制Origin,任何站点均可向服务器发起连接,就存在CSWH(跨站Websocket劫持)

如果服务器没有限制Access-Control-Allow-Origin,那么任何域均可能取得服务器返回的信息,类似于CORS

Reference

https://www.freebuf.com/vuls/194714.html
http://sh1yan.top/2018/08/12/jsonp-study
https://www.jianshu.com/p/3a9ed30233f6
https://zhuanlan.zhihu.com/p/61044032