带有 PHP 标头的跨域请求标头 (CORS)

Posted

技术标签:

【中文标题】带有 PHP 标头的跨域请求标头 (CORS)【英文标题】:Cross-Origin Request Headers(CORS) with PHP headers 【发布时间】:2018-09-10 12:57:30 【问题描述】:

我有一个简单的 php 脚本,我正在尝试跨域 CORS 请求:

<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: *");
...

但我仍然收到错误:

Access-Control-Allow-Headers 不允许请求头字段X-Requested-With

我缺少什么吗?

【问题讨论】:

【参考方案1】:

正确处理 CORS 请求有点复杂。这是一个响应更全面(和正确)的函数。

/**
 *  An example CORS-compliant method.  It will allow any GET, POST, or OPTIONS requests from any
 *  origin.
 *
 *  In a production environment, you probably want to be more restrictive, but this gives you
 *  the general idea of what is involved.  For the nitty-gritty low-down, read:
 *
 *  - https://developer.mozilla.org/en/HTTP_access_control
 *  - https://fetch.spec.whatwg.org/#http-cors-protocol
 *
 */
function cors() 
    
    // Allow from any origin
    if (isset($_SERVER['HTTP_ORIGIN'])) 
        // Decide if the origin in $_SERVER['HTTP_ORIGIN'] is one
        // you want to allow, and if so:
        header("Access-Control-Allow-Origin: $_SERVER['HTTP_ORIGIN']");
        header('Access-Control-Allow-Credentials: true');
        header('Access-Control-Max-Age: 86400');    // cache for 1 day
    
    
    // Access-Control headers are received during OPTIONS requests
    if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') 
        
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
            // may also be using PUT, PATCH, HEAD etc
            header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
        
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
            header("Access-Control-Allow-Headers: $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']");
    
        exit(0);
    
    
    echo "You have CORS!";

安全注意事项

根据已批准的来源列表检查 HTTP_ORIGIN 标头。

如果来源未被批准,那么您应该拒绝该请求。

请阅读规范。

TL;DR

当浏览器想要执行跨站点请求时,它首先会通过对 URL 的“飞行前”请求确认这是否可行。通过允许 CORS,您是在告诉浏览器来自该 URL 的响应可以与其他域共享。

CORS 不会保护您的服务器。 CORS 试图通过告诉浏览器对与其他域共享响应的限制来保护您的用户。通常这种共享是完全禁止的,所以 CORS 是一种在浏览器的正常安全策略中戳破洞的方法。这些漏洞应尽可能小,因此请始终根据某种内部列表检查 HTTP_ORIGIN。

这里有一些危险,尤其是如果 URL 提供的数据通常受到保护。您实际上是在允许源自其他服务器的浏览器内容读取(并可能操纵)您服务器上的数据。

如果您要使用 CORS,请仔细阅读协议(它非常小)并尝试了解您在做什么。为此目的,代码示例中提供了一个参考 URL。

标题安全

已经观察到 HTTP_ORIGIN 标头是不安全的,这是真的。事实上,所有 HTTP 标头对于该术语的不同含义都是不安全的。除非标头包含可验证的签名/hmac,或者整个对话都通过 TLS 进行身份验证,否则标头只是“浏览器告诉我的东西”。

在这种情况下,浏览器说“来自域 X 的对象想要从该 URL 获得响应。可以吗?” CORS 的重点是能够回答“是的,我允许这样做”。

【讨论】:

请注意,将 HTTP Origin 值作为允许的来源发回将允许任何人使用 cookie 向您发送请求,因此可能会从登录您的站点然后查看攻击者页面的用户那里窃取会话。您要么想要发送“*”(这将禁止 cookie 从而防止会话窃取),要么想要发送您希望站点为其工作的特定域。 同意。在实践中,您可能不会允许任何旧域使用您的 CORS 服务,而是将其限制为您决定信任的某个集合。 唯一真正有效的!.. 只需将 Access-Control-Allow-Origin: * 更改为 Access-Control-Allow-Origin: $_SERVER['HTTP_ORIGIN'] 通过无条件地允许使用ACAC: true 的任何来源,您实际上是在将同源策略扔出窗外。从安全的角度来看,这个答案是一个糟糕的建议,应该被否决。 确实,$_SERVER['HTTP_ORIGIN] 并不“安全”,因为您的应用无法验证请求的真实来源。然而,浏览器的工作就是保护这个头部。您的应用并没有试图阻止来自不同组织的人们使用它。相反,您的应用正在向浏览器确认此 URL 可接受来自某些域的跨站点请求。【参考方案2】:

我遇到了同样的错误,并在我的后端脚本中使用以下 PHP 修复了它:

header('Access-Control-Allow-Origin: *');

header('Access-Control-Allow-Methods: GET, POST');

header("Access-Control-Allow-Headers: X-Requested-With");

【讨论】:

【参考方案3】:

Access-Control-Allow-Headers 不允许将* 作为接受值,请参阅 Mozilla 文档here。

您应该发送接受的标头而不是星号(第一个 X-Requested-With 如错误所示)。

更新:

* 现在被接受为Access-Control-Allow-Headers

根据MDN Web Docs 2021:

* 仅算作没有凭据的请求(没有 HTTP cookie 或 HTTP 身份验证信息的请求)的特殊通配符值。在带有凭据的请求中,它被视为文字标头名称*,没有特殊语义。请注意,Authorization 标头不能使用通配符,并且始终需要明确列出。

【讨论】:

截至 2021 年,看起来 * 现在已根据 MDN 文档被接受。【参考方案4】:

互联网上的许多描述都没有提到指定Access-Control-Allow-Origin 是不够的。这是一个适合我的完整示例:

<?php
    if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') 
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: POST, GET, DELETE, PUT, PATCH, OPTIONS');
        header('Access-Control-Allow-Headers: token, Content-Type');
        header('Access-Control-Max-Age: 1728000');
        header('Content-Length: 0');
        header('Content-Type: text/plain');
        die();
    

    header('Access-Control-Allow-Origin: *');
    header('Content-Type: application/json');

    $ret = [
        'result' => 'OK',
    ];
    print json_encode($ret);

【讨论】:

请解释为什么它还不够,什么最小的例子足够的。 不幸的是,我不记得确切,我现在没有时间再次调查它,但据我所知,网络服务器/浏览器方面的一些基本假设使其无法正常工作.这是对我有用的最小代码。 如果已经在 apache 的虚拟主机中发送了 ..那么只有这段代码可以工作 ..if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') die(); 基本上这里说的是足够,只是如果请求方法是'options'就不行。【参考方案5】:

我只是设法让 dropzone 和其他插件使用此修复程序(angularjs + php 后端)

 header('Access-Control-Allow-Origin: *'); 
    header("Access-Control-Allow-Credentials: true");
    header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
    header('Access-Control-Max-Age: 1000');
    header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token , Authorization');

将此添加到您的upload.php 或您将发送请求的位置(例如,如果您有upload.html 并且您需要将文件附加到upload.php,然后复制并粘贴这4 行)。 此外,如果您在 chrome/mozilla 中使用 CORS 插件/插件,请务必多次切换它们,以便启用 CORS

【讨论】:

【参考方案6】:

如果您想从 PHP 创建 CORS 服务,您可以将此代码用作文件中处理请求的第一步:

// Allow from any origin
if(isset($_SERVER["HTTP_ORIGIN"]))

    // You can decide if the origin in $_SERVER['HTTP_ORIGIN'] is something you want to allow, or as we do here, just allow all
    header("Access-Control-Allow-Origin: $_SERVER['HTTP_ORIGIN']");

else

    //No HTTP_ORIGIN set, so we allow any. You can disallow if needed here
    header("Access-Control-Allow-Origin: *");


header("Access-Control-Allow-Credentials: true");
header("Access-Control-Max-Age: 600");    // cache for 10 minutes

if($_SERVER["REQUEST_METHOD"] == "OPTIONS")

    if (isset($_SERVER["HTTP_ACCESS_CONTROL_REQUEST_METHOD"]))
        header("Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT"); //Make sure you remove those you do not want to support

    if (isset($_SERVER["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"]))
        header("Access-Control-Allow-Headers: $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']");

    //Just exit with 200 OK with the above headers for OPTIONS method
    exit(0);

//From here, handle the request as it is ok

【讨论】:

这解决了我的问题 - 显然我的 PHP 网络服务无法正确处理 OPTIONS 请求 - 我的 Angular 前端在发送 POST 请求之前依赖它。谢谢!【参考方案7】:

如果我们没有正确理解它的功能,CORS 可能会令人头疼。我在 PHP 中使用它们,它们可以正常工作。 reference here

header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Max-Age: 1000");
header("Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Cache-Control, Pragma, Authorization, Accept, Accept-Encoding");
header("Access-Control-Allow-Methods: PUT, POST, GET, OPTIONS, DELETE");

【讨论】:

我在 Codeigniter 4.1.3 中使用过它,它确实有效【参考方案8】:

当使用 angular 4 作为客户端并使用 PHP 作为服务器端时,这么多代码对我来说很有效。

header("Access-Control-Allow-Origin: *");

【讨论】:

使用“*”通配符时要小心。除非你真的打算这样做,否则永远不要打开它。至于测试您的 Angular 应用程序,请指定 localhost:4200,它会在更安全的同时工作。 在运行 PHP 7.4.x 的 LAMP 服务器上测试【参考方案9】:

这应该可以工作

header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Cache-Control, Pragma, Authorization, Accept, Accept-Encoding");

【讨论】:

【参考方案10】:

将此代码添加到 .htaccess

在标头中添加自定义身份验证密钥,如 app_key、auth_key..etc

Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers: "customKey1,customKey2, headers, Origin, X-Requested-With, Content-Type, Accept, Authorization"

【讨论】:

以上是关于带有 PHP 标头的跨域请求标头 (CORS)的主要内容,如果未能解决你的问题,请参考以下文章

带有 jquery.ajax() 的跨域“授权”标头

使用 CORS 的 OPTIONS 请求宁静的跨域

IE8/IE9 中的跨域标头

带有自定义标头的跨域 jquery ajax api 调用未命中服务器

具有自定义标头和请求正文作为 JSON 字符串的跨域 jquery ajax 请求

访问 CORS 请求的响应标头