CORS跨域资源共享漏洞-一次崎岖的学习记录
本文最后更新于 2024年10月21日 晚上
先来首音乐:whoa
同源策略
同源策略简介
同源策略(Same-Origin Policy)是浏览器的一种安全机制,用于限制一个源(域名、协议或端口号的组合)的文档或脚本如何与来自另一个源的资源进行交互。具体来说,同源策略阻止不同源之间的网页通过脚本访问对方的资源或执行恶意操作,这有助于保护用户数据安全和隐私。
根据同源策略的规定,浏览器只允许来自同一源的文档之间共享资源(如脚本、样式表和图片),而不允许跨源的文档直接进行读取和操作。这种策略有效地防止了跨站点脚本攻击(XSS)和跨站点请求伪造(CSRF)等安全威胁。
需要注意的是,同源策略仅在浏览器环境中实施,不影响服务器之间的通信。为了在浏览器中进行跨域资源访问,开发者可以通过一些方法如 CORS(跨域资源共享)、JSONP(JSON with Padding)或使用服务器代理来规避这些限制,但需要确保操作的安全性。
总之,同源策略是浏览器为了保护用户信息和防止安全风险而设立的一道安全屏障,对网页开发和安全性有着重要影响。
同源的要求
- 协议
- 域名
- 端口
同时满足这三种条件就是同源,当存在两个站点,其中有一项不满足相同条件的时候,我们即可说这两个站点不是同源站点,而当其中一个站点想请求另外一个站点的资源的时候我们边称它为跨域请求
,而由于安全考虑,跨域请求
会受到同源策略的限制
不受影响的标签
- script
- img
- iframe
- link
- css
CORS机制
简介
- 由于同源策略的严格限制,有时会妨碍合法的跨域通信需求的场景。CORS(Cross-Origin Resource Sharing,跨域资源共享)机制正是为了解决这个问题而引入的。CORS是H5提供的一种机制,WEB应用程序可以通过在HTTP增加字段来告诉浏览器,哪些不同来源的服务器是有权访问本站资源的,当不同域的请求发生时,就出现了跨域。
可能需要使用CORS跨域的场景
- 前后端分离的应用:
- 在前后端分离的架构中,前端(通常由JavaScript框架如React、Angular、Vue等构建)和后端(例如Node.js、Django、Spring等)通常托管在不同的域名或端口上。这种情况下,前端需要通过跨域请求来与后端服务器进行数据交换。
- 第三方API调用:
- 应用程序需要使用第三方提供的API服务,例如社交媒体API(Facebook、Twitter)、地图服务API(Google Maps、Mapbox)、支付网关API(PayPal、Stripe)等。由于这些API通常托管在不同的域名上,使用时需要进行跨域请求。
- CDN(内容分发网络)资源加载:
- 静态资源(如图像、脚本、样式表等)常常托管在CDN上。浏览器需要跨域请求从CDN加载这些资源,以提高加载速度和性能。
- 微服务架构:
- 在微服务架构中,不同的服务通常托管在不同的域名或端口上。前端应用或API网关需要跨域请求这些服务以完成各种业务逻辑。
- 跨域数据共享:
- 多个应用程序可能需要共享用户数据或其他信息。例如,一个单点登录(SSO)系统需要跨域访问不同的子域名或独立应用程序以验证用户身份。
- 嵌入式内容:
- 网站可能嵌入来自不同源的内容,例如嵌入YouTube视频、Google Maps、社交媒体分享按钮等。这些嵌入内容需要跨域请求以加载和展示。
- 跨域脚本与iframe通信:
- 不同源的页面通过iframe嵌套在一起,可能需要进行跨域通信。例如,广告脚本、支付窗口、聊天窗口等。通过postMessage API,可以在不同源之间安全地传递消息。
- 单页应用(SPA):
- 单页应用通常在客户端渲染,并通过AJAX或Fetch请求后端API获取数据。如果后端API托管在不同域名或端口上,需要进行跨域通信。
与CORS相关的HTTP头部
请求头
Origin:
- 解释: 请求的来源域名。
- 示例:
Origin: http://example.com
- 作用: 浏览器在跨域请求时会自动包含这个头字段,用于指示请求的来源。服务器可以根据这个字段决定是否允许该请求。
Access-Control-Request-Method:
- 解释: 预检请求中使用的方法。
- 示例:
Access-Control-Request-Method: POST
- 作用: 在预检请求(preflight request)中,浏览器会发送这个头字段以通知服务器实际请求将使用的方法(如GET、POST、PUT等)。
Access-Control-Request-Headers:
- 解释: 预检请求中使用的自定义头字段。
- 示例:
Access-Control-Request-Headers: X-Custom-Header
- 作用: 在预检请求中,浏览器会发送这个头字段以通知服务器实际请求将包含的自定义头字段。
响应头
Access-Control-Allow-Origin:
- 解释: 允许的请求来源域名。
- 示例:
Access-Control-Allow-Origin: http://example.com
- 作用: 服务器在响应中包含这个头字段以指示允许哪些域名访问资源。如果值为
*
,表示允许所有域名访问。
Access-Control-Allow-Methods:
- 解释: 允许的HTTP方法。
- 示例:
Access-Control-Allow-Methods: GET, POST, PUT
- 作用: 服务器在响应中包含这个头字段以指示允许的HTTP方法。
Access-Control-Allow-Headers:
- 解释: 允许的自定义头字段。
- 示例:
Access-Control-Allow-Headers: X-Custom-Header
- 作用: 服务器在响应中包含这个头字段以指示允许的自定义头字段。
Access-Control-Allow-Credentials:
- 解释: 是否允许发送凭证(cookies、HTTP认证信息等)。
- 示例:
Access-Control-Allow-Credentials: true
- 作用: 服务器在响应中包含这个头字段以指示是否允许客户端发送凭证。如果设置为
true
,则允许。
Access-Control-Expose-Headers:
- 解释: 允许客户端访问的响应头字段。
- 示例:
Access-Control-Expose-Headers: X-Custom-Header
- 作用: 服务器在响应中包含这个头字段以指示哪些响应头字段可以被客户端脚本访问。
Access-Control-Max-Age:
- 解释: 预检请求结果的缓存时间(以秒为单位)。
- 示例:
Access-Control-Max-Age: 3600
- 作用: 服务器在响应中包含这个头字段以指示浏览器可以缓存预检请求的结果多长时间。****
CORS漏洞危害
CORS(跨源资源共享)漏洞可能导致多种安全隐患,主要包括:
- 信息泄露:攻击者可以通过恶意网站获取用户的敏感信息,如 cookies、会话令牌等。
- 跨站请求伪造(CSRF):恶意网站可以通过 CORS 请求伪造用户的请求,导致用户在不知情的情况下执行操作。
- 窃取用户数据:如果 API 对 CORS 配置不当,攻击者可能直接访问用户数据。
- 权限提升:攻击者可以利用 CORS 漏洞执行高权限操作,获取未授权的资源。
- 账户接管:通过获取敏感信息,攻击者可以接管用户账户,进行进一步的攻击。
CORS漏洞分析
Demo搭建
注意:攻击者接收端网站与受害者端网站要满足不同源,否则就不是在研究CORS问题了
被攻击端demo
- admin.php
- 部署在www.x1lys.com
1 |
|
该demo模拟一个正常的后台登录成功后的简易界面,设置Access-Control-Allow-Origin: *,Access-Control-Allow-Credentials: true,设置了cookie,响应中返回登录后的敏感信息
攻击端demo
- hacker.html
- 部署在test.hacker.com
1 |
|
该demo使用fetch函数对http://www.x1lys.com/admin.php发起跨域请求,并包含凭证,解析得到响应内容,输出敏感信息在控制台日志与页面上,并将cookie弹窗显示,设置了诱惑性的内容"深夜天堂-在线观看!点击开始观看!",真实环境中再做一些18禁渲染,就更具有诱惑性了
漏洞分析
假设目标网站是http://www.x1lys.com,黑客的接收网站是http://test.hacker.com
根据请求与响应的几种情况,可判断是否存在CORS漏洞:
Access-Control-Allow-Origin | Access-Control-Allow-Credentials | 结果 |
---|---|---|
* | true | 不存在漏洞 |
test.hacker.com(任意origin的URL) | true | 存在漏洞 |
www.x1lys.com(固定的可信任的URL) | true | 安全-不存在漏洞 |
null | true | 存在漏洞 |
1. Allow-Origin为 * ,Allow-Credentials为 true
Allow-Origin为 * 也就是允许所有的源与目标网站跨域通信,Allow-Credentials为true也就是允许传输凭证(cookie,token,jwt等等)看似肯定存在漏洞,因为Access-Control-Allow-Origin的值允许所有的源跨域通信,当然可设置为攻击者接收敏感信息的接收端网站。但是CORS规范明确规定,凭证请求不能与通配符 *
一起使用,浏览器在处理包含凭证(如 Cookies、HTTP 认证)的请求时,会自动拦截这种允许所有的源与目标网站跨域通信的配置情况,于是往往利用不成功,于是就不存在漏洞
受害者端demo
1 |
|
demo演示
失败,失败的原因翻译一下就是:从来源 ‘http://test.hacker.com‘ 访问 ‘http://www.x1lys.com/admin.php‘ 已被 CORS 策略阻止:当请求的凭证模式为 ‘include’ 时,响应中的 ‘Access-Control-Allow-Origin’ 头的值不能是通配符 ‘*’
2. Allow-Origin为<all-host> ,Allow-Credentials为 true
Allow-Origin为<all-host>,是指Origin的值是什么,Allow-Origin就是什么。Allow-Credentials为true也就是允许传输凭证(cookie,token,jwt等等)。这种一定存在漏洞,因为Access-Control-Allow-Origin的值可控,可设置为攻击者接收敏感信息的接收端网站,且不再受CORS规范的限制了
受害者端demo-1
1 |
|
demo演示
成功接收了敏感信息!
但是为什么cookie是空的呢?显然,cookie接收失败了,原因在于cookie与其他响应中的敏感信息不同,cookie专门有一些保护机制,之所以接收到的cookie是空的,就是以下浏览器的cookie保护机制起了作用:
cookie保护机制-SameSite属性
SameSite
属性是一个用于控制 Cookie 行为的安全机制,主要用来防止跨站请求伪造(CSRF)攻击。它可以指定 Cookie 在不同场景下的发送规则。SameSite
属性有三个可能的值:
Strict:
- 描述:最为严格,Cookie 仅在同源请求中发送。如果用户通过其他站点的链接访问该网站,Cookie 将不会被发送。
- 适用场景:对于安全性要求极高的应用,比如支付页面。
Lax:
- 描述:当开发开发人员没有设置samesite的值得时候,Lax是默认值,规则稍稍放宽,大多数情况也是不发送。Cookie 在同源请求和某些跨站请求中发送,比如通过 GET 请求导航到该站点时。对于安全性要求较高的场景,
Lax
是一个较好的选择。 - 适用场景:适用于大多数网站,允许用户通过外部链接访问时仍能携带 Cookie。
一次网页请求就可以看成AJAX请求,在Lax限制下,cookie也是不能被发送的
- 描述:当开发开发人员没有设置samesite的值得时候,Lax是默认值,规则稍稍放宽,大多数情况也是不发送。Cookie 在同源请求和某些跨站请求中发送,比如通过 GET 请求导航到该站点时。对于安全性要求较高的场景,
None:
- 描述:Cookie 在所有请求中发送,无论是同源还是跨站请求。使用
None
时,必须将Secure
设置为true
,以确保 Cookie 仅通过 HTTPS 连接发送。 - 适用场景:需要跨站点共享 Cookie 的情况,例如第三方服务集成。
- 描述:Cookie 在所有请求中发送,无论是同源还是跨站请求。使用
看到这里就应该明白了为什么刚刚那个cookie的值为空了吧,就是SameSite属性搞的鬼,因为没有设置SameSite,于是就使用默认值:Lax,而Lax模式是禁止了该形式的cookie发送的,于是cookie的值为空
于是需要特定情况才可以接收cookie:SameSite属性为None,且必须将 Secure
设置为 true
,以确保 Cookie 仅通过 HTTPS 连接发送,于是需要把两个站点都改为HTTPS的
使用phpstudy2018搭建https站点
准备:
- 先安装openssl:https://slproweb.com/products/Win32OpenSSL.html,选择win64 amd
- 添加环境变量:/PATH/bin
一、创建自签名证书
打开命令提示符(CMD),使用 OpenSSL 创建证书:
1
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt
按提示填写相关信息。
将生成的
server.key
和server.crt
文件复制到 PHPStudy 的apache\conf\
目录下。
二、配置 Apache 支持 HTTPS
打开 PHPStudy,点击 Apache 的配置按钮,找到
httpd.conf
文件。在
httpd.conf
文件中,添加以下内容,确保以下行没有被注释:1
2LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf找到并打开
httpd-ssl.conf
文件(通常在conf/extra/
目录下),并根据需要修改以下部分:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# 如果遇到错误,多半是443被占用了,换一个端口就行,或者检查一下配置文件的语法错误
Listen 9443
<VirtualHost _default_:9443>
DocumentRoot "D:/phpStudy/PHPTutorial/WWW/cors"
ServerName www.x1lys.com
SSLEngine on
SSLCertificateFile "D:/phpStudy/PHPTutorial/apache/conf/server.crt"
SSLCertificateKeyFile "D:/phpStudy/PHPTutorial/apache/conf/server.key"
</VirtualHost>
Listen 9444
<VirtualHost _default_:9444>
DocumentRoot "D:/phpStudy/PHPTutorial/WWW/cors"
ServerName test.hacker.com
SSLEngine on
SSLCertificateFile "D:/phpStudy/PHPTutorial/apache/conf/server.crt"
SSLCertificateKeyFile "D:/phpStudy/PHPTutorial/apache/conf/server.key"
</VirtualHost>确保将
"你的项目路径"
替换为你的实际项目路径。
三、配置 hosts 文件
打开
C:\Windows\System32\drivers\etc\hosts
文件,添加以下行:1
127.0.0.1 www.x1lys.com
4. 重启 PHPStudy
- 在 PHPStudy 界面中,重启 Apache 服务以使配置生效。
5. 访问 HTTPS 网站
- 打开浏览器,访问
https://www.x1lys.com
,如果有安全警告,可以选择继续访问(因为是自签名证书)。
四、注意事项
- 浏览器信任:自签名证书会被浏览器标记为不安全,可以在浏览器中手动信任。
- 证书有效期:自签名证书通常有效期为一年,可以根据需要重新生成。
受害者端demo-2
1 |
|
1 |
|
demo演示
我的443端口被占用了,换成了9443端口和9444端口
Google Chrome
没有被浏览器策略拦截,但是接收的cookie仍然为空
Edge
先是被我自装的防广告的插件ADGuard拦截了
然后被Edge拦截了,控制台提示SSL证书未被浏览器信任
火狐
也被拦截了,控制台仅提示未成功,未给出原因
继续挣扎-切换php版本
一番搜索查询,大概搞清楚状况了。原来我的PHP版本是5.4.45的,但是PHP 版本低于 7.3,不支持 SameSite
属性,好嘛破案了,在phpstudy2018改一下php版本
结果发现:
好家伙,他就没有7.3版本的哈哈,我服了呀,可能phpstudy2018版本太老了,你问我为什么不用小皮面板,其实我之前一直用的小皮面板,但是觉得小皮面板不如这个老版本的简洁好用。phpstudy2018什么切换版本、修改配置的功能一目了然,于是就没用小皮面板了
于是为了继续使用phpstudy2018(懒得下载小皮再从头到尾配置一遍两个站点了),我选择手动给phpstudy2018装上php-7.3以上的版本:
步骤 1:下载 PHP
- 访问 PHP 官网:前往 PHP 官方网站 下载所需的 PHP 版本(建议选择 PHP 7.4 或 8.x 的线程安全版)。
- 选择版本:选择7.3以上的版本并下载 Zip 包
步骤 2:解压 PHP
- 解压缩文件:将下载的 Zip 文件解压缩到你的计算机上,例如解压到 D:\phpStudy\PHPTutorial\php 目录。
- 重命名文件夹:将解压后的文件夹重命名为合适的版本名称,例如 D:\phpStudy\PHPTutorial\php\php-7.4.1
步骤3:修改配置
- 找到httpd-php.conf,一般在D:\phpStudy\PHPTutorial\Apache\conf\extra目录下
- 直接修改一个原版本的路径,改为新添加的版本的路径,如下
- 原版本php-7.2.1-nts
1 |
|
- 直接修改其路径,改为:php-7.4.1
1 |
|
步骤 5:验证安装
- 切换版本时,还是看不到新添加的版本,但是不要急,直接选择原版本php-7.2.1-nts
- 查看phpinfo
- php-7.4.1,手动添加成功!
继续挣扎-导入自签名证书
Google Chrome
- 打开设置
- 点击右上角的三个点图标(菜单)并选择“设置”。
- 进入隐私和安全设置
- 在左侧的菜单中选择“隐私和安全”。
- 管理证书
- 点击“安全”选项,然后点击“管理证书”。
- 导入证书
- 在“证书”窗口中,选择“导入”。
- 按照导入向导的提示操作,选择您要导入的自签名证书文件(通常是 .crt 或 .pem 文件)。
- 选择将证书放入“受信任的根证书颁发机构”存储区。
- 完成向导,证书就会被导入。
Mozilla Firefox
- 打开设置
- 点击右上角的汉堡图标(三条横线)并选择“设置”。
- 进入隐私和安全设置
- 在左侧的菜单中选择“隐私和安全”。
- 查看证书
- 滚动到“隐私和安全”部分的底部,找到“证书”部分,然后点击“查看证书”。
- 导入证书
- 在证书管理器中,选择“导入”。
- 浏览并选择您要导入的自签名证书文件。
- 选择合适的信任设置(如将证书信任为网站的证书颁发机构)。
- 确认并完成导入。
Microsoft Edge
- 打开设置
- 点击右上角的三个点图标(菜单)并选择“设置”。
- 进入隐私和安全设置
- 在左侧的菜单中选择“隐私、搜索和服务”。
- 管理证书
- 滚动到“安全”部分并点击“管理证书”。
- 导入证书
- 在“证书”窗口中,选择“导入”。
- 按照导入向导的提示操作,选择您要导入的自签名证书文件。
- 选择将证书放入“受信任的根证书颁发机构”存储区。
- 完成向导,证书就会被导入。
悲惨结局
做了这两个操作后,还是如上三张图片所示,毫无变化……
或许是证书无效的问题,或许是三大cookie保护机制(httponly,samesite,secure)的阻碍,或许浏览器版本太高了,有了一些更严格的安全策略使得我我们无法窃取到cookie,那这一种分析就先暂时到此为止吧,以后遇到再作详细分析
3.Allow-Origin为 ,Allow-Credentials为 true
这种就不说了,这种就是开发已经考虑到了这个CORS安全问题,已经把Allow-Origin写死了,固定为了安全的可信任的url,这种就是安全正确的做法,如果没有写死,而是采用一些正则匹配啥的来过滤的话,还可以尝试绕过一下……
4.Allow-Origin为 null ,Allow-Credentials为 true
这里写得很清楚了
https://xz.aliyun.com/t/12001?time__1311=GqGxRGiti%3Dd052x%2BxCwrbmD8Fqu40IYox
CORS防御与修复
防御和修复 CORS 漏洞的措施包括:
1. 严格设置 CORS 策略
- 限制允许的源:只允许特定的可信源访问 API,避免使用通配符(
*
)。 - 使用正确的 HTTP 方法:仅允许必要的 HTTP 方法(如 GET、POST),限制其他不必要的方法。
2. 使用 Preflight 请求
- 对复杂请求使用 OPTIONS 方法,确保服务器确认允许的请求头和方法。
3. 验证请求头
- 确保仅接受必要的请求头,避免不必要的信息泄露。
4. 设置安全的 Cookie 属性
- 使用
HttpOnly
和Secure
属性,防止 JavaScript 访问 cookie,并确保在 HTTPS 下传输。
5. 实施认证和授权
- 确保所有敏感操作都经过适当的身份验证和权限验证。
6. 监控和日志记录
- 记录跨域请求和响应,监控可疑活动,及时发现潜在攻击。
7. 定期审计和测试
- 定期进行安全审计和渗透测试,识别和修复 CORS 配置中的潜在漏洞。
参考:https://xz.aliyun.com/t/12001?time__1311=GqGxRGiti%3Dd052x%2BxCwrbmD8Fqu40IYox
如有错误,欢迎各位大佬在评论区交流,随时指教小弟