freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

postMessage常见安全问题
2024-09-04 16:26:22

什么是 postMessage

postMessage 是一种跨源通信的方法不受“同源策略”限制,允许不同源的网页之间进行安全通信,常用的跨域通信方法还包括 CORS、JSONP。它广泛应用于许多现代Web应用中,比如嵌入的iframe和跨窗口的消息传递。

其中同源策略是重要的安全策略,同源指的是协议、域名、端口都相同。非同源的两个资源间不能通信。

postMessage跨源通信不受“同源策略”限制的前提需要发送方需要有接收方window的引用。同时发送方调用接收方window的postMessage方法发送消息,接收方的消息监听器收到消息进行后续处理。

postMessage导致的安全问题及修复

未指定接收方origin

1725434744_66d80b7802cc57d377f39.png!small?1725434744764

潜在攻击场景造成上图存在安全风险原因主要是发送方(内部 iframe)直接向 top 发送消息,而不是其直接父级。使用了通配符 "*" 作为 targetOrigin,允许任何源接收消息。消息内容包含敏感信息(URL、调用栈、cookie 等)。

在复杂的嵌套 iframe 结构中建议明确指定目标源,只向直接父窗口发送消息而不是 直接向top发送消息,最小化通过 postMessage 传输的敏感信息如上面图中的cookie等。

// 发送消息
window.postMessage("hello", "*");

// 接收消息
window.addEventListener("message", function(event) {
  console.log("Message received: ", event.data);
});

当你使用 postMessage 将数据发送到其他窗口时,始终指定精确的目标 origin,而不是 *,就比如上面的示例代码和图中的错误示范会导致恶意网站可以在你不知情的情况下更改窗口的位置,因此它可以拦截使用 postMessage 发送的数据。

消息传输中的postMessage数据{"data":"hello", "origin": "https://test.com/", "source": a},其中包括data: postMessage的第一个参数发送信息,origin: 发送方的源(协议、域名、端口),source: 发送方window的引用为可选内容。

没有验证消息的来源

parseMessage: function(e) {
    e = "append::<style onload=\"eval(self.name)\">:"
    if ("string" == typeof e) {
        var r = e.split("::"), t = r[2], ["append", "<style onload=\"eval(self.name)\">"]
        , n = r[0], n = "append"
        , i = r[1], "<style onload=\"eval(self.name)\">"
        try {
            n[n](i)
        } catch (a) {}
    }
},
...
append: function(e) {
    try {
        e = decodeURIComponent(e),
        $("body").append(e)
    } catch (t) {}
},

提供 append 接口给页面写入内容,但没有验证消息来源只要收到消息就进行处理。从任意域 iframe调用 frames[0].postMessage("append::aaa", "*"),就可以往控制台页面写入aaa,写入XSS代码就可以执行任意恶意JS。

假设有一个页面使用类似这样的代码来处理 postMessage:

window.addEventListener('message', function(event) {
    parseMessage(event.data);
});

这里没有对 event.origin 进行任何检查,直接将接收到的数据传给 parseMessage 函数。

攻击者从任意域iframe发送如下信息就会导致parseMessage 函数解析这个消息,append 函数会将解码后的内容添加到body中最终导致执行恶意 JavaScript 代码。

targetWindow.postMessage('append::<style onload="eval(alert(\'XSS via postMessage\'))">', '*');

修复方式:

始终验证 postMessage 的来源,对接收到的数据进行严格的验证和清理,避免直接执行或插入接收到的数据

window.addEventListener('message', function(event) {
    if (event.origin !== "https://trusted-domain.com") return;
});

验证origin的方法可绕过

1.正则表达式缺少起止符、点号没转义:

let re = /imgcache.test.com|user.openqa.test.com|www.dnspod.cn/
if(re.test(e.origin)){
}

问题分析:

缺少起止符(^ 和 $):允许部分匹配

点号 (.) 没有转义:在正则表达式中,点号匹配任意字符

绕过方式:

arbitrary.imgcache.test.com.subdomain.example.com
利用了缺少起止符,只要包含匹配字符串即可

imgcacheatest.com
利用了点号没转义,"a" 可以匹配任意字符

imgcacheatestacom.example.com
结合了上述两个问题

修复方式:

let re = /^(imgcache\.test\.com|user\.openqa\.test\.com|www\.dnspod\.cn)$/
if(re.test(e.origin)){
}

2.endsWith 匹配子域名没加点:

if(e.origin.endsWith('test.com')){
}

问题分析:

未考虑到域名边界,任何以 'test.com' 结尾的字符串都会被匹配

绕过方式:

arbitrarytest.com
添加任意前缀的域名虽然不是test.com的子域名但仍然通过了检查

修复方式:

在使用正则表达式验证时,务必使用准确的模式匹配,包括起止符和正确的转义。

使用 endsWith 等方法时,需要考虑到域名的结构,确保只匹配预期的域名。

最安全的方法是使用精确匹配,例如维护一个允许的源列表,并进行完全相等的比较。

if(e.origin.endsWith('.test.com') || e.origin === 'test.com'){
}


# web安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录