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']);(顺便说一句:不太推荐)并且您得到了错误的标题。我打赌原因之一:

Web 服务器会覆盖标头。 浏览器使用缓存,虽然它不应该。 (BFC)。 您没有将更新后的代码上传到请求服务器。

让我们从头开始。

不要相信自己,请确认。 (我没看过一次,我们不工作,然后反映我没有上传更改:))。 在浏览器调试器中关闭缓存。 (您必须打开调试器)如果这是一个问题并且该函数要在池中可用,请在请求中添加时间戳。 检查nginx/apache/server panel的配置

你知道你使用的结构是Access-Control-Allow-Origin: *的同义词吗?您应该检查HTTP_ORIGIN 是否属于允许的池。

【讨论】:

以上是关于CORS 标头在浏览器中被更改,导致内容被阻止的主要内容,如果未能解决你的问题,请参考以下文章

spring webflux CORS 标头被删除

跨域请求在 Firefox 中被阻止

被 CORS 策略阻止:预检响应中的 Access-Control-Allow-Headers 不允许请求标头字段内容类型 [重复]

CORS 在 dockerized Django REST + Angular 项目中被阻止

预检浏览器请求期间的 CORS 问题 [重复]

访问被 CORS 标头阻止