CORS 标头在浏览器中被更改,导致内容被阻止
Posted
技术标签:
【中文标题】CORS 标头在浏览器中被更改,导致内容被阻止【英文标题】:CORS Headers are altered in the browser resulting in content becoming blocked 【发布时间】:2019-07-29 01:07:36 【问题描述】:Update 2(完整的日志集)
从客户的角度来看
请求头:
POST /dev/micro_server.php HTTP/1.1 主机:production-server.com 连接:保持活动 内容长度:86 编译指示:无缓存 缓存控制:无缓存 接受:文本/html,/; q=0.01 来源:https://debug.dev 用户代理:Mozilla/5.0(X11;Linux x86_64) AppleWebKit/537.36(KHTML,如 Gecko)Chrome/71.0.3578.98 Safari/537.36 OPR/58.0.3135.90 内容类型:application/x-www-form-urlencoded;字符集=UTF-8 推荐人:https://debug.dev/ 接受编码:gzip、deflate、br 接受语言:en-US,en;q=0.9 Cookie:debugger_session=iq4tbdk374mtvodsd3edcf2jq5
响应头:
HTTP/1.1 200 正常 服务器:nginx/1.4.6 (Ubuntu) 日期:格林威治标准时间 2019 年 3 月 12 日星期二 12:01:27 内容类型:text/html 传输编码:分块 连接:保持活动 X-Powered-By:PHP/5.5.9-1ubuntu4.17 访问控制允许方法:GET、POST、OPTIONS 访问控制允许来源:https://production-server.com 访问控制允许凭据:true 过期:1981 年 11 月 19 日星期四 08:52:00 GMT 缓存控制:无存储、无缓存、必须重新验证、后检查=0、预检查=0 编译指示:无缓存 内容编码:gzip
控制台错误:
从源“https://debug.dev”访问“https://production-server.com/dev/micro_server.php”处的 XMLHttpRequest 已被 CORS 策略阻止:“Access-Control-Allow-Origin”标头的值“https://production-server.com”不等于提供的原产地。
控制台警告:
跨域读取阻止 (CORB) 阻止了 MIME 类型为 text/html 的跨域响应 https://daikai.no/dev/micro_server.php。详情请见https://www.chromestatus.com/feature/5629709824032768。
从服务器的角度来看
这是服务器所说的它已接收和发送的内容(检查执行登录更新 1 的代码):
Array
(
[req] => Array
(
...
[HTTP_ORIGIN] => https://debug.dev
...
)
[rsp] => Array
(
[0] => X-Powered-By: PHP/5.5.9-1ubuntu4.17
[1] => Access-Control-Allow-Origin: https://debug.dev
[2] => Access-Control-Allow-Methods: GET, POST, OPTIONS
[3] => Access-Control-Allow-Credentials: true
)
)
更新
我在服务器上添加了一些日志记录,脚本现在以这些行开头:
# allow access from other domains
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Credentials: true");
$all = [
'req' => $_SERVER,
'rsp' => headers_list()
];
$s = print_r($all, true);
$p = '/var/www/path/to/file_' . uniqid() . '.txt';
file_put_contents($p, $s);
这样,我可以确认请求以正确的 Origin 到达服务器,并且服务器发回正确的 CORS 标头。然而,开发者控制台中的Access-Control-Allow-Origin
是错误的,请求被阻塞了。
这是使用上述代码获得的精简日志:
Array
(
[req] => Array
(
...
[HTTP_ORIGIN] => https://debug.dev
...
)
[rsp] => Array
(
[0] => X-Powered-By: PHP/5.5.9-1ubuntu4.17
[1] => Access-Control-Allow-Origin: https://debug.dev
[2] => Access-Control-Allow-Methods: GET, POST, OPTIONS
[3] => Access-Control-Allow-Credentials: true
)
)
问题
当收到的实际标头是Access-Control-Allow-Origin: https://debug.dev
时,Access-Control-Allow-Origin
如何以及为什么更改为https://production.com
?
(原帖)
背景
我在本地开发机器上安装了一个基于 Web 的调试工具。在我的 /etc/hosts 中有一个条目,我已经为它分配了域 debug.dev
。我还添加了本地 CA 机构,并成功为域名创建了 SSL 证书,所以现在我可以在浏览器中打开 https://debug.dev/
并且调试工具正常打开。
这个工具应该适用于登台和生产服务器。所以它需要向其他域发送 AJAX 请求。我可以完全控制这些服务器,并且我正在从这些服务器发回 CORS 标头,如下所示:
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
header("Access-Control-Allow-Credentials: true");
问题
现在我面临一个莫名其妙的情况,当我向生产服务器发送 AJAX 请求时,我得到了 Wrong 带有 SERVER 域的 CORS 标头,如下所示:
访问控制允许凭据:true 访问控制允许来源:https://production-server.com
但是,如果我右键单击并使用 在新选项卡中打开,CORS 标头就是它们应该是的;即
访问控制允许凭据:true 访问控制允许来源:https://debug.dev
据我所知,请求之间的唯一区别是第一个请求作为 AJAX POST 请求发送,因此发送 HTTP_X_REQUESTED_WITH 标头,而第二个请求作为普通 GET 请求发送。这会导致服务器返回不同的 CORS 标头吗?
【问题讨论】:
还提供浏览器控制台的完整输出。 (也启用info
消息)还显示所有发送和接收的 OPTIONS 请求标头。
很可能没有足够的信息来回答这个问题......例如。错误的服务器提供的页面(拔下电缆,它可能无法访问)。一个快速而肮脏的解决方法是在同一个标题中返回两个主机名......或者只是使用xdebug
进入它。
我被束缚住了,没能早点回答。 @Soleil,不,它们在不同的机器上。 debug.dev 位于本地计算机 (127.0.0.1) 上,production-server.com 是 Digitalocean 液滴。
@MartinZeitler 所有证据,包括登录 production-server.com 都表明服务器正在发送正确的标头。在接收端,虽然我们得到了 Origin 标头的不同值。我会尽量让问题更清楚。
@MajidFouladpour 日志记录不一定确认请求命中此实例(除非记录时间戳和 IP)。虽然在那里设置断点肯定会。客户端应该如何重写标头的值......甚至不知道其他主机名?在整个代码库中搜索header
和硬编码的主机名可能也是我会尝试的,因为理论上可以覆盖标头的值。
【参考方案1】:
问题可能类似于my answer here:
服务器未配置为使用正确的“Access-Control-Allow-”标头响应 OPTIONS 请求。
在新标签页中打开 url 是一个 GET 请求并且正在工作,因为它没有发出预检请求,因为它满足由CORS documentation 定义的simple request 的标准
另一方面,ajax 请求 是一个 POST 请求并且符合 Preflighted request 的条件,这意味着 preflight OPTIONS 请求应该是先做。
简而言之,您已经正确设置了 CORS 响应标头,但服务器未配置为为 OPTIONS 方法请求添加这些标头。
解决方案是在2xx响应的服务器代码上处理OPTIONS请求,并添加**Access-Control-Allow-就像你对 GET 和 POST 请求所做的那样。请记住,OPTIONS 请求不包含任何参数,因此应在任何验证或请求解析之前完成。
另外,根据Access-Control-Allow-Origin documentation:
如果服务器指定单个来源而不是“*”通配符,则服务器还应在 Vary 响应标头中包含 Origin — 以向客户端指示服务器响应将根据 Origin 请求标头的值而有所不同。
所以也设置 Vary 响应标头:
例如在你的脚本顶部尝试:
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS')
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Credentials: true");
header("Vary: Origin");
exit;
参考文献
Preflighted requests
response for preflight 403 forbidden
【讨论】:
【参考方案2】:问题是您使用header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
(顺便说一句:不太推荐)并且您得到了错误的标题。我打赌原因之一:
让我们从头开始。
不要相信自己,请确认。 (我没看过一次,我们不工作,然后反映我没有上传更改:))。 在浏览器调试器中关闭缓存。 (您必须打开调试器)如果这是一个问题并且该函数要在池中可用,请在请求中添加时间戳。 检查nginx/apache/server panel的配置你知道你使用的结构是Access-Control-Allow-Origin: *
的同义词吗?您应该检查HTTP_ORIGIN
是否属于允许的池。
【讨论】:
以上是关于CORS 标头在浏览器中被更改,导致内容被阻止的主要内容,如果未能解决你的问题,请参考以下文章
被 CORS 策略阻止:预检响应中的 Access-Control-Allow-Headers 不允许请求标头字段内容类型 [重复]