问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
前端攻防之解读浏览器同源策略
渗透测试
前端攻防之解读浏览器同源策略。
前端攻防之解读浏览器同源策略 ============== 0x0 前言 ------ 笔者本以为之前简单浏览了下概念,就理解了浏览器的同源策略,后面经过一些实际的案例,才发现其实自己只是知其皮毛,不知其全貌,故有了本文文章,与读者一起分享。 0x1 同源策略定义 ---------- firefox的技术文档: [https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin\_policy](https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy) RFC 中英文(机翻): <https://rfc2cn.com/rfc6454.html> 理解**同源策略(Same Origin Policy, SOP)**: > 同源策略是一种"网络浏览器"(不单单指常规浏览器)的安全机制,旨在防止不同网站相互攻击。 > > 同源策略限制了从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。 同源的定义: > 判断两个URL是否同源,主要根据3个条件进行判断: `protocol`(协议: https/http/file...) 、 `port`(端口)和`host`主机名,需要三个条件同时满足,缺一不可,两个URL才属于同源。 0x2 同源策略意义 ---------- > 当浏览器从一个源向另一个源发送 HTTP 请求时,与另一个域相关的任何 cookie(包括身份验证会话 cookie)也作为请求的一部分发送。这意味着响应将在用户会话中生成,并包含特定于用户的任何相关数据。如果没有同源策略,如果您访问了恶意网站,它将能够读取您来自 GMail 的电子邮件、来自 Facebook 的私人消息等。 大白话: 如果没有同源策略,将不同网站的敏感数据进行有效的隔离,那么只要你一不小心打开了一个恶意站点,那么这个站点就能窃取你浏览器上其他网站(购物网站、支付平台...等等)的信息,也就是说如果没有同源策略这一限制,用户使用浏览器上网冲浪毫无隐私和安全可言。 0x3 同源策略机制 ---------- 浏览器在来自不同来源的元素之间可能发生交互的情况都会应用来源检查。这包括但不限于: > Javascript代码和文档对象模型(DOM),例如,页面无法访问其iframe的内容,除非它们具有相同的来源。 > 数据层面:LocalStorage、IndexDB、Cookie, 例如,您特定站点的会话cookie不能发送到具有不同来源的页面。 > 网络层面:AJAX调用(XmlHTTPRequest) > > 这些交互通常可分为三类: > > (1)跨域写操作。一般是被允许的*。*例如链接(links),重定向以及表单提交 > > (2)跨域资源嵌入。一般是被允许,下面RFC的1)点提到。 > > (3)跨域读操作。 一般是不被允许的。 正如RFC中的规范: 1\) SOP并没有完全限制不同源的网络资源访问,通常,只是禁止从其他来源读取信息,但是,允许检索和加载不同来源的某些资源。比较经典的例子,就是引用第三方站点提供的jquery类的javascript脚本、加载其他站点的图片、应用其他站点的样式表和iframe标签引入第三方html资源等。 2\) 同源策略,只是禁止读取不同源的内容,但是允许向其他来源发送信息,即可以对不同源发送请求,但没办法读取响应。这个实现也是CSRF攻击的一个原理,也就是说同源策略,对CSRF而言是无效的,这也说明使用token/验证码作为防护才是CSRF的正确解决方案。 0x4 示例讲解 -------- 上面小节主要介绍同源策略的主体内容,但有一些特殊情况,需要用实例进行说明。 本机配置`/etc/hosts`: ```bash 127.0.0.1 a.a.xq17.com 127.0.0.1 a.xq17.com 127.0.0.1 b.b.xq17.com 127.0.0.1 b.xq17.com 127.0.0.1 www.xq17.com 127.0.0.1 xq17.com ``` ### 0x4.1 经典误区 引用mozilla.org的例子,对比域名:<http://store.company.com/dir/page.html> | URL | 结果 | 原因 | |---|---|---| | `http://store.company.com/dir2/other.html` | 同源 | 只有路径不同,协议、端口、主机相同。 | | `http://store.company.com/dir/inner/another.html` | 同源 | 只有路径不同,协议、端口、主机相同。 | | `https://store.company.com/secure.html` | 失败 | 协议不同 | | `http://store.company.com:81/dir/etc.html` | 失败 | 端口不同 ( `http://` 默认端口是80) | | `http://news.company.com/dir/other.html` | 失败 | 主机不同 | 上面列举出来的表格例子,根据同源策略的判断规则,很容易判断出是否同源。 那么,让我们考虑另外一种情况,<http://a.xq17.com/> 与 <http://xq17.com/> 是否同源,按照同源策略规则来判断,很容易知道这两个的主机名并不同,所以说它们并不是同源的,但是我以前存在一些误区,下面展开来说。 > 引用: > > <https://developer.mozilla.org/zh-CN/docs/Web/API/Document/domain> > > 关于设置`document.domain`的值,对跨域的影响。 > > ![image-20220205200925482](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-4e3c9df0c1e5937cca37df9d105afc81162c012d.png) > > 这个文档理解起来不是那么直观,这也是造成我误区的一个原因。 误区情况1: 不能向不同源的站点发出请求。 ![image-20220205205339313](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-8f7c4ba0af4f29e282a0f17e317cb00d4bf464c2.png) **正确情况1: 可以发出请求,但没办法读取响应的内容。** 误区情况2: 可以任意设置domain的值,且只能设置一级子域名。 **正确情况2: domain属性的值只能设置为它的上级域名,但可以一直到一级子域名。** ![image-20220205202421628](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-064acc9481a4a382e8823db1ed816e39364651dd.png) 误区情况3: <http://xq17.com/> 在默认的情况下,获取到的是`xq17.com`,那么如果我们在可控的`http://a.xq17.com/`的页面设置`document.domain='xq17.com'`则能够跨过同源策略的限制。 **答案: 经过实验,这个想法是错误的,浏览器判断一致性,并不是简单的比较值,因为重新赋值该值的时候,端口会被设为NULL,所以并不能匹配上。这种跨域需要同时控制两个站点同时设置`document.domain`相同值。** 1)<http://xq17.com> ```html <html> <head> <title>index.html</title> <script type="text/javascript"> // document.domain = 'xq17.com'; var flag= "flag:{ xq17.com}"; </script> </head> <body> <h1> xq17.com flag: </h1> <input value="flag:{ xq17.com}"> </body> </html> ``` <http://a.xq17.com> ```html <html> <head> <title>a.html</title> <iframe src="http://xq17.com/" id="frame"></iframe> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"> </script> <script type="text/javascript"> document.domain = 'xq17.com'; function load(){ console.log(frame.contentWindow.flag); } </script> </head> <body> <h1> a.xq17.com flag: </h1> <input value="flag:{a.xq17.com}"> </body> </html> ``` 在Chrome和Firefox最新版测试,直接提示不同源码: ![image-20220205203714166](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-0ed89ff3d282854d17f722d756f840b65086317e.png) 我们修改下`http://xq17.com`的内容为手动设置`document.domain = 'xq17.com'`。 ```html <html> <head> <title>index.html</title> <script type="text/javascript"> document.domain = 'xq17.com'; var flag= "flag:{ xq17.com}"; </script> </head> <body> <h1> xq17.com flag: </h1> <input value="flag:{ xq17.com}"> </body> </html> ``` 这样的话,就能`a.xq17.com`就能访问到`xq17.com`的DOM了。 ![image-20220205204028304](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-74ed357d98fc26ba6391db92a3ab2f98f88b1b12.png) ### 0x4.2 跨域读取 Cookie python FLask: 分别注册三个域名: [http://a.xq17.com、http://b.xq17.com、http://xq17.com](http://a.xq17.xn--comhttp-0o3f//b.xq17.com%E3%80%81http://xq17.com) ```python from flask import Blueprint from flask import Flask a = Blueprint('a', __name__) b = Blueprint('b', __name__) main = Blueprint('main', __name__) @a.route('/') def homeA(): return 'hello flask A' @b.route('/') def homeB(): return 'hello flask B' @main.route('/') def homeMain(): return 'hello flask Main' app = Flask(__name__) app.config['SERVER_NAME'] = 'xq17.com' app.register_blueprint(a, subdomain='a') app.register_blueprint(b, subdomain='b') app.register_blueprint(main, subdomain='') app.run(port=80) ``` 正常来说,跨域是不能够读取Cookie,那么为什么有时候我们读取子域的cookie可以获取到主域的Cookie信息? 如果关键的敏感站点,不设置domain,`set-cookie`: ```python @main.route("/set_cookie") def set_cookie(): resp = Response("success") ''' 设置cookie,默认有效期是临时cookie,浏览器关闭就失效 可以通过 max_age 设置有效期, 单位是秒 ''''' # resp.set_cookie('flag', 'key pass', path='/', domain='.xq17.com') resp.set_cookie('flag', 'key pass', path='/') return resp ``` 那么默认cookie只能由当前访问的URL的主机读取,其他子域由于同源策略,是没办法直接读取到的。 那么平时我们可以注意到一个现象,比如我们在`login.redacted.com`登陆,那么之后我们访问`my.redacted.com`会自动保持登陆状态,也就是cookie从login域共享到了my域下,讲道理,同源策略是不允许这种访问的,但是浏览器对Cookie比较宽松,我们可以通过设置Cookie的`domain`属性来实现在某个域下的共享Cookie,也就是绕开了同源策略这一关。 > 1.在setcookie中省略domain参数,那么domain默认为当前域名。 > > 2.domain参数可以设置父域名以及自身,但不能设置其它域名,包括子域名,否则cookie不起作用 ```python @main.route("/set_cookie") def set_cookie(): resp = Response("success") ''' 设置cookie,默认有效期是临时cookie,浏览器关闭就失效 可以通过 max_age 设置有效期, 单位是秒 ''''' resp.set_cookie('flag', 'key pass', path='/', domain='.xq17.com') # resp.set_cookie('flag', 'key pass', path='/') return resp ``` ![image-20220205232658380](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-f3c2f98fbee070bce2818bdeab8be51411359510.png) 然后我们去`http://a.xq17.com`查看是否能读取到这个Cookie。 ![image-20220205232743195](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-3a8799a13a6528c29d2922f5917010a4c4854065.png) ### 0x4.3 window.name 正如提到:<https://developer.mozilla.org/zh-CN/docs/Web/API/Window/name> ![image-20220206002017003](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-987393fc6a91b67f7aa6e357061b7aff45d35d15.png) > window.name 可无视同源策略,具备跨域通信的能力,在一个窗口的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每一个页面对window.name都有读写的权限,window.name是持久的存在于一个窗口载入的所有页面中的,并不会因为新的页面的载入而被重置。 这里举一个例子进行说明如何通过window.name实现跨域数据交换。 <http://b.xq17.com/data.html> ```html <script type="text/javascript"> window.name = "data.html sensitive data -> a.xq17.com" </script> ``` <http://a.xq17.com/getData.html> ```html <html> <head> <script type="text/javascript"> function getData(){ var iframe = document.getElementById('proxy'); iframe.src = 'a.xq17.com/a.html'; iframe.onload = function(){ var data = iframe.contentWindow.name; //上述即为获取iframe里的window.name也就是data.html页面中所设置的数据; console.log(data); }; } </script> <iframe id="proxy" src="http://b.xq17.com/data.html" style="display: none;" onload = "getData()"> </head> </html> ``` 访问:<http://b.xq17.com/getData.html> ![image-20220206004236749](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-d3aa0690667af64a44e49b75cc69e84bf4d9eb06.png) 跨域原理: 首先敏感信息存在b.xq17.com这个域名,我们在a.xq17.com域下先iframe引用,这个时候iframe的`contentWindow.name`就已经存放了我们想要的敏感数据,但是因为同源策略,我们还不能直接读取到,所以我们需要重新设置`iframe.src='a.xq17.com/a.html'`,这样的话,就可以访问iframe里面的`window.name`了,这个过程,`widnow.name`是全局的,并没有发生改变。 ### 0x4.4 利用思路 经过前面的分析,如果我们想要获取到比较关键的Cookie,由于同源策略的限制,是比较难的,所以对于像Twiter这种社交网站的核心XSS漏洞,很值钱其实是可以理解的,毕竟你要挖到社交关键网站的直接域下的XSS漏洞,才能直接利用到Cookie。要不然,遇到一些特殊的XSS点我们可能就需要利用一些骚思路不断横向来绕过同源策略了,比如观察某个站点是不是设置了`document.domain`进行与其他站点跨域名,`window.name`有没有存储敏感内容或者观察有没有关键的cookie键值共享到了其他子域下面。通过这样的方式来扩大我们的攻击面,提高最终拿到关键的Cookie信息成功率。 0x5 跨域方法 -------- 同源策略提高安全性的同时,也给web前端开发带来一些苦恼,所以有很多人研究出了很多种跨域的奇技淫巧,例如上面提到的`document.domain`,但是这些用法的安全性和规范性实在一言难尽,实际的生产环境中使用的很少,下面笔者则介绍3种较为常用的跨域方法,并分析其中可能导致的安全问题。这也可以说是同源策略衍生的漏洞了,毕竟这些都是为了解决同源策略带来的问题而引出的解决方案。 ### 0x5.1 CORS [跨源资源共享(CORS)](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS) [CORS技术指南paper](https://www.bedefended.com/papers/cors-security-guide) [浅析CORS攻击及其挖洞思路](https://xz.aliyun.com/t/7242) > 可知CORS的优先级是高于同源策略的,也就是CORS是缓解同源策略的一个措施,决定是否可以对跨源的资源携带Cookie请求并判断是否接收响应包。 以前浏览器没有引入SameSiste这个机制,CORS可跨域攻击成功的header头一般形如如下形式: ![image-20220206143115618](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-f89dd7fe6c7e4e0a110a6d02e4a661abdd902d08.png) 下面是典型的nginx的CORS配置不当,因为没有对origin来源进行判断,故允许任意第三方域携带Cookie发起请求,并且能够支持读取到响应,从而访问到资源: ```bash Access-Control-Allow-Origin $http_origin; Access-Control-Allow-Credentials true; ``` Python - cors.py: ```python @main.route("/credential") def get_credential(): resp = make_response("flag:sensitive flag") origin = request.headers.get('Origin') resp.headers['Access-Control-Allow-Origin'] = origin resp.headers['Access-Control-Allow-Credentials'] = 'true' return resp ``` 我们直接在a.xq17.com域下的Console发起带Cookie的跨越请求。 ```javascript var script = document.createElement("script"); script.type = "text/javascript"; script.src = "https://libs.baidu.com/jquery/2.0.0/jquery.min.js"; document.body.appendChild(script); $.ajaxSetup({crossDoamin:true, xhrFields: {withCredentials: true}}); $.get("http://xq17.com/credential",function(data){ console.log(data); }); ``` ![image-20220206150204262](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-e5b41721de9fa8598ba676e30dbf11c74938871e.png) 但是如果我们不在a.xq17.com下,而是在其他源下,由于SameSite机制,默认为Lax,跨站的情况,会导致没办法携带Cookie对外发起请求。 ```javascript var script = document.createElement("script"); script.type = "text/javascript"; script.src = "https://libs.baidu.com/jquery/2.0.0/jquery.min.js"; document.body.appendChild(script); $.ajaxSetup({crossDoamin:true, xhrFields: {withCredentials: true}}); $.get("https://xq17.com/credential",function(data){ console.log(data); }); ``` ![image-20220206184251167](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-27a82dd8226ca752cbb49659726b575eea516801.png) Python Flask使用https: ```bash mkdir cert && cd cert openssl genrsa -des3 -out server.key 1024 openssl req -new -key server.key -out server.csr openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt # 修改下app的启动 , 使用系统证书,提前安装 pip3 install pyOpenSSL app.run(port=443, ssl_context='adhoc') ####################### ``` 经过测试,无论是http还是https, samesite都会对Cookie进行限制,samesite虽然跟同源策略没有直接关系,但是它对Cookie的限制比同源策略的跨域判断,做了更详细的跨站约定。 ### 0x5.2 jsonp jsonp(json with padding),可以说是一种非官方推荐的,比较取巧,且实际应用广受好评的跨域解决方案。 jsonp 跨域原理: > 前面可知,同源策略允许跨域资源的嵌入,jsonp则是利用`<script>`的`src`属性去加载跨域的数据,因为`<script>`加载的内容需要符合`javascript`语法,恰好javascript天生支持json形式的数据,但是我们不能直接读取到返回的数据内容,所以我们需要用一个函数名将数据包起来,然后通过执行该函数来得到返回数据,这就是jsonp中padding部分。 jsonp 实现机制: > 首先在客户端创建一个回调函数,将回调函数的名称(例如callback)传给服务端; > > 服务端生成json数据data后,创建js文件,将callback(data)(data成了callback的参数)写入js文件; > > 客户端创建script文件,链接地址为此前生成的js文件地址; > > 解析script文件,返回的callback(data)即调用callback函数(前面已创建) jsonp 优缺点: > **JSONP的优点:** > > 不受同源策略的限制; > > 兼容性好,在老的浏览器中可以运行; > > 请求完毕后可以通过调用 callback 的方式回传结果。 > > **JSONP的缺点:** > > 只支持 `GET` 这种HTTP请求; > > jsonp在调用失败的时候不会返回各种HTTP状态码; > > 安全性:callback传入的参数是在后端进行了一次拼接,这即代表存在注入的可能,如果后端设计不当,是有可能出现安全风险的。 了解更多JSONP: [JSONP 维基百科](https://zh.wikipedia.org/wiki/JSONP) 漏洞示例: ```python @b.route('/jsonp', methods=["GET", "POST"]) def jsonp(): callback = request.args.get("callback", '') data = {"flag": "There is sensitive data!"} return_value = callback + "(" + json.dumps(data) + ")" return return_value ``` EXP: ```javascript function exp(data){ console.log(data); } var script = document.createElement("script"); script.type = "text/javascript"; script.src = "http://b.xq17.com/jsonp?callback=exp"; document.body.appendChild(script); ``` 通过在不同源的站点,通过`script`的`src`引入`b.xq17.com/jsonp`接口的内容,虽然不能直接读取,但是可以调用。 ![image-20220206195529082](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-c137ee6fa2d53b6a5f08d41bbf40c32262c719f1.png) 但是像我上面这么简单的实现,不单单存在jsonp跨域漏洞,还存在XSS注入的风险。 ![image-20220206195928355](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-1b64a2687b41121ea27819fe1dbce2c1f4a4b055.png) 在真实的漏洞挖掘案例中,这种情况并不少见,一般来说,常见的修复方案: 1\) 限制返回的类型`content-type: application/javascript`,默认为`Content-Type: text/html; charset=utf-8` 2\) 传入数据进行html实体化,避免引入`<`这些直接可解析的符号。 ### 0x5.3 window.postMessage 引用:[window.postMessage](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage) > **window.postMessage()**方法可以安全地实现跨源通信。 > > 通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为https),端口号(443为https的默认值),以及主机 (两个页面的模数 [`Document.domain`](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/domain)设置为相同的值) 时,这两个脚本才能相互通信。 > > **window.postMessage()**方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。 > > 晦涩的翻译: > > 从广义上讲,一个窗口可以获得对另一个窗口的引用(比如 `targetWindow = window.opener`),然后在窗口上调用 `targetWindow.postMessage()` 方法分发一个 [`MessageEvent`](https://developer.mozilla.org/zh-CN/docs/Web/API/MessageEvent) 消息。接收消息的窗口可以根据需要自由[处理此事件 (en-US)](https://developer.mozilla.org/en-US/docs/Web/Events)。传递给 window.postMessage() 的参数(比如 message )将[通过消息事件对象暴露给接收消息的窗口](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage#The_dispatched_event)。 关于window.postMessage的用法还是比较简单的。 ```javascript otherWindow.postMessage(message, targetOrigin, [transfer]); ``` > **otherWindow** > > 其他窗口的一个引用,比如iframe的contentWindow属性、执行[window.open](https://developer.mozilla.org/en-US/docs/Web/API/Window/open)返回的窗口对象、或者是命名过或数值索引的[window.frames](https://developer.mozilla.org/en-US/docs/Web/API/Window/frames)。 > > **message** > > 将要发送到其他 window的数据, 数据会自动被序列化。 > > **targetOrigin** > > 通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是\**字符串"*"(表示无限制)或者一个URI\*\*。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口; > > PS.这个参数如果配置不当,那么就会存在跨域。 > > **transfer** 可选参数 > > 是一串和message 同时传递的 [`Transferable`](https://developer.mozilla.org/zh-CN/docs/Web/API/Transferable) 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。 > > PS.很少使用到,不展开介绍。 ```php window.addEventListener(type, listener, options); ``` > **type** > > 表示监听[事件类型](https://developer.mozilla.org/zh-CN/docs/Web/Events)的字符串。[message (en-US)](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/message_event),表示通过 WebSocket 接收到一条消息。 > > **listener** > > 当所监听的事件类型触发时,会接收到一个事件通知(实现了 [`Event`](https://developer.mozilla.org/zh-CN/docs/Web/API/Event) 接口的对象)对象。`listener` 必须是一个实现了 [`EventListener`](https://developer.mozilla.org/zh-CN/docs/Web/API/EventListener) 接口的对象,或者是一个[函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Functions)。 > > **options** (可选) > > 一个指定有关 `listener`属性的可选参数**对象**。Boolean类型 例子: **同源页面Message交互,example.html** ```html <script type="text/javascript"> /* * A窗口的域名是http://a.xq17.com/,以下是A窗口的script标签下的代码: */ let msg = {url : "http://a.xq17.com/"}; window.postMessage(msg, '*'); function receiveMessage(event) { console.log(event.data); alert(event.data.url); } window.addEventListener("message", receiveMessage, false); </script> ``` 通过发送和监听全局window的窗口信息,可以实现信息交互。 **跨源交互** postMessage.html ```html <html> <head> <meta charset="utf-8"> <title>window.postMessage()跨域消息传递</title> </head> <body> <div> <input id="text" type="text" value="test message..." /> <button id="send" >发送消息</button> </div> <iframe id="receiver" src="http://b.xq17.com/receiveMessage.html" width="500" height="60"> </iframe> <script> window.onload = function() { var receiver = document.getElementById('receiver').contentWindow; var btn = document.getElementById('send'); btn.addEventListener('click', function (e) { e.preventDefault(); // TargetOrigin 设为* 不安全 var val = document.getElementById('text').value; receiver.postMessage("Hello "+val+"!", "*"); }); } </script> </body> </html> ``` receiveMessage.html ```html <html> <head> <meta charset="utf-8"> <title>接收消息</title> </head> <body> <div id="message"> Hello World! </div> <script> window.onload = function() { var messageEle = document.getElementById('message'); window.addEventListener('message', function (e) { // 对e.origin 防止接收到恶意第三方站点传递的信息。 alert(e.origin); messageEle.innerHTML = "从"+ e.origin +"收到消息: " + e.data; }); } </script> </body> </html> ``` 这个html页面,在通过JS监听窗口,然后将传入值拼接到并`innerHTML`到页面中,可以导致XSS漏洞。 ![image-20220206221455009](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-fefe8bbdd1f084e18555424a7b482bbce16fea58.png) 除了通过控制传入的内容,我们也可以反过来角色,如果网站将敏感的信息`postMessage`出来,且没有限制`event.origin`那么我们也可以通过这种方式去劫持一些敏感的凭证。 0x6 SameSite ------------ 前面在CORS部分提到了SameSite这个安全机制,这个其实跟同源策略没有直接关系,主要与是否限制发送Cookie有关,SameSite的目的主要是防止CSRF攻击,但是为什么SameSite会对CORS攻击也能起到缓解的作用呢?下面来看看吧! ### 0x6.1 认识SameSite **`SameSite`** 是HTTP响应头 [`Set-Cookie`](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie) 的属性之一。它允许您声明该Cookie是否仅限于第一方或者同一站点上下文。 `SameSite` 接受下面三个值: > [`Strict`](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie/SameSite#strict) > > Cookies只会在第一方上下文中发送,不会与第三方网站发起的请求一起发送。 > > [`Lax`](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax) > > Cookies允许与顶级导航一起发送,并将与第三方网站发起的GET请求一起发送。这是浏览器中的默认值。 > > | 请求类型 | 示例 | Lax情况 | > |---|---|---| > | 链接 | `<a href="..."></a>` | 发送 Cookie | > | 预加载 | `<link rel="prerender" href="..."/>` | 发送 Cookie | > | GET 表单 | `<form method="GET" action="...">` | 发送 Cookie | > | POST 表单 | `<form method="POST" action="...">` | 不发送 | > | iframe | `frameLabelStart--frameLabelEnd` | 不发送 | > | AJAX | `$.get("...")` | 不发送 | > | Image | `<img src="...">` | 不发送 | > > [`None`](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie/SameSite#none) > > Cookie将在所有上下文中发送,即允许跨域发送。 > > 以前 `None` 是默认值,但最近的浏览器版本将 `Lax` 作为默认值,以便对某些类型的跨站请求伪造 ([CSRF](https://developer.mozilla.org/zh-CN/docs/Glossary/CSRF)) 攻击具有相当强的防御能力。 > > 使用 `None` 时,需在最新的浏览器版本中使用 [`Secure`](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie) 属性。 ### 0x6.2 浏览器兼容性 Chrome: > Chrome 51 开始,浏览器的 Cookie 新增加了一个`SameSite`属性,主要用于防止CSRF攻击和用户追踪。 > > 在Chrome 80之后,`SameSite`默认值的由None改变为Lax,正式对开发者提出了挑战。 Firefox: > Firefox 从 v60 开始支持 SameSite 属性 > > Firefox 96,强制执行 Cookie 策略:默认情况下 Same-Site=lax,它为跨站点请求伪造 (CSRF) 攻击提供了坚实的第一道防线。 > > 可以通过about:config,修改`laxByDefault`为false进行sameSite策略。 > > ![image-20220206224428519](https://shs3.b.qianxin.com/attack_forum/2022/02/attach-0b15470e6a234d0e904a8b98112440646a1e1419.png) ### 0x6.3 跨站与跨域 跨域很简单,就是前文提到的,三元组判断(协议,主机,端口) SameSite不同的值对应的行为与是否跨站是直接关联的。 > 顶级域名(Top-level domains)(TLDs)是如 `.com` 等的一系列域名列表。 > > 在大部分场景下,站(Site)的定义是 TLDs+1,如 `.com` 的下一级,如 `github.com`。 > > 但 Github 也会为不同的开发者提供不同的 `github.io` 域名,如 `a.github.io` 和 `b.github.io`,为了区分这两个域名,Mozilla 引入了有效顶级域名(Effective top-level domains)(eTLDs)的概念。 > > 因此,站(Site)是有效顶级域名的下一级域名,即 eTLDs+1。 比如:`a.web.dev` 和 `b.web.dev` 属于同站,而 `a.github.io` 和 `b.github.io` 属于跨站。 这里eTLDs的情况有点特殊,一般来说,我们可以根据TLDS,也就是主域名是否相同,来判断是否属于同站即可。 像对于`github.io`、`ac.cn`这些特殊域名,可以从 [https://publicsuffix.org/list/public\_suffix\_list.dat](https://publicsuffix.org/list/public_suffix_list.dat) 找找看,是否属于eTLDS再进行判断。 ### 0x6.4 解答疑问 由上面的分析可知,SameSite在目前最新的浏览器中的值默认为Lax,那么我们使用CORS 漏洞的时候,需要发出ajax的请求,这个时候由于SameSite的限制,请求是没有带Cookie,虽然这样,我们仍然可以绕过同源策略请求一些不需要授权的资源,但是如果没有Cookie,那么我们是没办法获取到那些较为敏感的授权信息的,那么显然就缓解了CORS漏洞的影响面。 0x7 总结 ------ 本文先介绍同源策略,再通过例子讲解同源策略的一些情况,接着介绍了跨域的三种常用方法,最后穿插介绍了SameSite这一安全机制与同源策略的微妙关系。可以看到,前端的常见漏洞,XSS/JSONP/CORS/CSRF 它们的关系其实非常微妙,有种围绕同源策略来衍生的感觉,因为同源策略在本质上决定了不同源站点资源是否能够共享,而这些常见的漏洞就是围绕着同源策略来做文章,达到造成不同程度的同源策略本意的破坏。 0x8 参考链接 -------- [same-origin-policy](https://portswigger.net/web-security/cors/same-origin-policy) [what-is-same-origin-policy](https://www.acunetix.com/blog/web-security-zone/what-is-same-origin-policy/) [同源策略](https://www.geekby.site/2021/01/%E5%90%8C%E6%BA%90%E7%AD%96%E7%95%A5/) [WebView跨源攻击分析](https://blogs.360.cn/post/webview%E8%B7%A8%E6%BA%90%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90.html) [同源策略、XSS和CSRF攻击](https://blog.wmuhua.com/2020/03/20/%E6%B5%8F%E8%A7%88%E5%99%A8%E5%8E%9F%E7%90%86/%E5%90%8C%E6%BA%90%E7%AD%96%E7%95%A5%E3%80%81XSS%E5%92%8CCSRF%E6%94%BB%E5%87%BB/) [01-跨域和跨站的基本概念](https://alexzhong22c.github.io/2020/05/22/cross-origin-cross-site/) [跨域方式及其产生的安全问题](https://xz.aliyun.com/t/4470) [SameSite Cookies 解读](https://vivaxyblog.github.io/2021/03/07/samesite-cookies-explained-cn.html) [SameSite cookie 的说明](https://web.dev/i18n/zh/samesite-cookies-explained/) [通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过](https://www.anquanke.com/post/id/219088)
发表于 2022-02-21 09:38:53
阅读 ( 5668 )
分类:
WEB安全
1 推荐
收藏
0 条评论
请先
登录
后评论
xq17
11 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!