Access-Control-Allow-Origin通配符子域,端口和协议

Eli*_*lie 285 cors

我正在尝试为所有子域,端口和协议启用CORS.

例如,我希望能够从http://sub.mywebsite.com:8080/运行XHR请求到https://www.mywebsite.com/*

通常情况下,我想启用匹配(并限于)的来源请求:

//*.mywebsite.com:*/*

mon*_*sur 234

CORS规范是全有或全无.它只支持*,null或者确切的协议+域+端口:http://www.w3.org/TR/cors/#access-control-allow-origin-response-header

您的服务器需要使用正则表达式验证原始标头,然后您可以在Access-Control-Allow-Origin响应标头中回显原始值.

  • CORS规范不支持OP的_exact_用例,这是非常短视的. (114认同)
  • @Noyo - 我将澄清我原来的意思.CORS规范并不严格要求所有实现CORS的服务器为OP的_exact_用例提供自动内置支持,这是非常短视的.让每个用户使用自定义PHP代码构建自己的填充程序,重写规则,或者你有什么是碎片,错误和灾难的秘诀.服务器开发者应该比这更清楚; 如果不这样做,CORS规范应该强制它们. (17认同)
  • @Dexter"null"可用于响应"null"原点,例如,当从file:// scheme发出CORS请求时. (7认同)
  • @aroth:不是,规范允许实现使用他们想要的*any*匹配语法;*实施*不支持这个用例只会是非常短视的.换句话说,您为服务器指定的内容不是ACAO值,后者只是协议详细信息.我的假设是,有一个安全场景需要或从原点被回馈中受益,但是一个天真的实现只需说"OK"就行了. (5认同)
  • 2015年更新:此答案应被视为有害,因为它不完整,可能会导致缓存问题.请参阅下面的答案,以获得正确的实施(适用于Apache)和解释:http://stackoverflow.com/a/27990162/357774.另外,@ aroth,正如tne指出的那样,规范实际上_does_允许OP的确切用例:http://www.w3.org/TR/cors/#resource-implementation.正如这个答案所指出的那样,由服务器来实现它.这可以在3行中完成,如上面提到的答案中所示. (3认同)
  • 而不是“精确域”,它应该是“起源”,因为它是协议+域+端口的组合。 (2认同)

Noy*_*oyo 186

根据DaveRandom的回答,我也在玩,并发现了一个稍微简单的Apache解决方案,它可以产生相同的结果(Access-Control-Allow-Origin动态设置为当前特定协议+域+端口),而不使用任何重写规则:

SetEnvIf Origin ^(https?://.+\.mywebsite\.com(?::\d{1,5})?)$   CORS_ALLOW_ORIGIN=$1
Header append Access-Control-Allow-Origin  %{CORS_ALLOW_ORIGIN}e   env=CORS_ALLOW_ORIGIN
Header merge  Vary "Origin"
Run Code Online (Sandbox Code Playgroud)

就是这样.

那些想要在父域(例如mywebsite.com)上启用CORS以及其所有子域的人可以简单地用这一个替换第一行中的正则表达式:

^(https?://(?:.+\.)?mywebsite\.com(?::\d{1,5})?)$.

注意:对于规范合规性和正确的缓存行为,ALWAYS总是Vary: Origin为启用CORS的资源添加响应头,即使对于非CORS请求和来自不允许源的请求也是如此(请参阅示例原因).

  • 你把这段代码放在哪里?.htaccess 还是在 apache 虚拟主机配置中? (5认同)
  • 我们几乎遇到过这种情况(不是 Vary Origin),并且当访问者使用相同字体在多个子域之间跳转时会出现不良行为。字体和 access-control-origin 标头也被缓存。我对此做了一点更改:如果请求来自我们允许的域之一,我使用“Access-Control-Allow-Origin *”。也许这是通过我们以前没有的“Vary Origin”解决的……现在也添加了它。 (2认同)
  • 不适用于主域"mywebsite.com" (2认同)

Dav*_*dom 58

编辑:使用@ Noyo的解决方案而不是这个.它更简单,更清晰,并且在负载下可能性能更高.

原始答案仅限于此处用于历史目的!!


我做了一些解决这个问题,并提出了可与Apache一起使用的可重用的.htaccess(或httpd.conf)解决方案:

<IfModule mod_rewrite.c>
<IfModule mod_headers.c>
    # Define the root domain that is allowed
    SetEnvIf Origin .+ ACCESS_CONTROL_ROOT=yourdomain.com

    # Check that the Origin: matches the defined root domain and capture it in
    # an environment var if it does
    RewriteEngine On
    RewriteCond %{ENV:ACCESS_CONTROL_ROOT} !=""
    RewriteCond %{ENV:ACCESS_CONTROL_ORIGIN} =""
    RewriteCond %{ENV:ACCESS_CONTROL_ROOT}&%{HTTP:Origin} ^([^&]+)&(https?://(?:.+?\.)?\1(?::\d{1,5})?)$
    RewriteRule .* - [E=ACCESS_CONTROL_ORIGIN:%2]

    # Set the response header to the captured value if there was a match
    Header set Access-Control-Allow-Origin %{ACCESS_CONTROL_ORIGIN}e env=ACCESS_CONTROL_ORIGIN
</IfModule>
</IfModule>
Run Code Online (Sandbox Code Playgroud)

只需将ACCESS_CONTROL_ROOT块顶部的变量设置为根域,如果它与您的域匹配,它将Origin:Access-Control-Allow-Origin:响应头值中将请求头值返回给客户端.

另请注意,您可以使用sub.mydomain.comas ACCESS_CONTROL_ROOT,它将限制原点sub.mydomain.com*.sub.mydomain.com(即它不必是域根).允许变化的元素(协议,端口)可以通过修改正则表达式的URI匹配部分来控制.


Pra*_*ala 16

我正在回答这个问题,因为接受的答案与主域名不匹配,只适用于子域名.此外,正则表达式分组是性能损失,这是不必要的.

例如:在http://somedomain.mywebsite.com/工作时,它不会为http://mywebsite.com发送CORS标头

SetEnvIf Origin "http(s)?://(.+\.)?mywebsite\.com(:\d{1,5})?$" CORS=$0

Header set Access-Control-Allow-Origin "%{CORS}e" env=CORS
Header merge  Vary "Origin"
Run Code Online (Sandbox Code Playgroud)

要为您的站点启用,只需在上面的Apache配置中将您的站点放在"mywebsite.com"的位置即可.

允许多个站点:

SetEnvIf Origin "http(s)?://(.+\.)?(othersite\.com|mywebsite\.com)(:\d{1,5})?$" CORS=$0
Run Code Online (Sandbox Code Playgroud)

测试部署后:

以下卷曲响应应在更改后具有"Access-Control-Allow-Origin"标头.

curl -X GET -H "Origin: http://examplesite1.com" --verbose http://examplesite2.com/query
Run Code Online (Sandbox Code Playgroud)


Lar*_*ars 9

我需要一个只有PHP的解决方案,所以万一有人也需要它.它需要一个允许的输入字符串,如"*.example.com",如果输入匹配,则返回请求标头服务器名称.

function getCORSHeaderOrigin($allowed, $input)
{
    if ($allowed == '*') {
        return '*';
    }

    $allowed = preg_quote($allowed, '/');

    if (($wildcardPos = strpos($allowed, '*')) !== false) {
        $allowed = str_replace('*', '(.*)', $allowed);
    }

    $regexp = '/^' . $allowed . '$/';

    if (!preg_match($regexp, $input, $matches)) {
        return 'none';
    }

    return $input;
}
Run Code Online (Sandbox Code Playgroud)

以下是phpunit数据提供程序的测试用例:

//    <description>                            <allowed>          <input>                   <expected>
array('Allow Subdomain',                       'www.example.com', 'www.example.com',        'www.example.com'),
array('Disallow wrong Subdomain',              'www.example.com', 'ws.example.com',         'none'),
array('Allow All',                             '*',               'ws.example.com',         '*'),
array('Allow Subdomain Wildcard',              '*.example.com',   'ws.example.com',         'ws.example.com'),
array('Disallow Wrong Subdomain no Wildcard',  '*.example.com',   'example.com',            'none'),
array('Allow Double Subdomain for Wildcard',   '*.example.com',   'a.b.example.com',        'a.b.example.com'),
array('Don\'t fall for incorrect position',    '*.example.com',   'a.example.com.evil.com', 'none'),
array('Allow Subdomain in the middle',         'a.*.example.com', 'a.bc.example.com',       'a.bc.example.com'),
array('Disallow wrong Subdomain',              'a.*.example.com', 'b.bc.example.com',       'none'),
array('Correctly handle dots in allowed',      'example.com',     'exampleXcom',            'none'),
Run Code Online (Sandbox Code Playgroud)

  • `preg_quote()` 将引用 * 符号,因此 `str_replace()` 会留下一个孤立的“\”。 (2认同)