Access-Control-Allow-Origin wildcard subdomains, ports and protocols
我正在尝试为所有子域,端口和协议启用CORS。
例如,我希望能够从http://sub.mywebsite.com:8080/运行XHR请求到https://www.mywebsite.com/*
通常情况下,我想启用匹配(并限于)的来源请求:
CORS规范是全有或全无。它只支持
您的服务器需要使用正则表达式验证原始标头,然后您可以在Access-Control-Allow-Origin响应标头中回显原始值。
基于DaveRandom的回答,我也在玩,并发现了一个稍微简单的Apache解决方案,它产生相同的结果(
1 2 3 | 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" |
就是这样。
那些想要在父域(例如mywebsite.com)上启用CORS以及其所有子域的人可以简单地用第一行替换第一行中的正则表达式:
注意:对于规范合规性和正确的缓存行为,始终为启用CORS的资源添加
编辑:使用@ Noyo的解决方案而不是这个。它更简单,更清晰,并且在负载下可能性能更高。
原始答案仅限于此处用于历史目的!!
我做了一些解决这个问题,并提出了可与Apache一起使用的可重用的.htaccess(或httpd.conf)解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <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> |
只需将块顶部的
另请注意,您可以使用
我正在回答这个问题,因为接受的答案与主域名不匹配,只适用于子域名。此外,正则表达式分组是性能损失,这是不必要的。
例如:在http://somedomain.mywebsite.com/工作时,它不会为http://mywebsite.com发送CORS标头
1 2 3 4 | 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" |
要为您的站点启用,只需在上面的Apache配置中将您的站点放在"mywebsite.com"的位置即可。
允许多个站点:
1 | SetEnvIf Origin"http(s)?://(.+\.)?(othersite\.com|mywebsite\.com)(:\d{1,5})?$" CORS=$0 |
测试部署后:
以下卷曲响应应在更改后具有"Access-Control-Allow-Origin"标头。
1 | curl -X GET -H"Origin: http://examplesite1.com" --verbose http://examplesite2.com/query |
我需要一个只有PHP的解决方案,所以万一有人也需要它。它需要一个允许的输入字符串,如"* .example.com",如果输入匹配,则返回请求标头服务器名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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; } |
以下是phpunit数据提供程序的测试用例:
1 2 3 4 5 6 7 8 9 10 11 | // <description> <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'), |
在.htaccess中设置
1 2 | SetEnvIf Origin"http(s)?://(.+\.)?domain\.com(:\d{1,5})?$" CRS=$0 Header always set Access-Control-Allow-Origin"%{CRS}e" env=CRS |
我尝试了其他几个建议的关键字
这是我的完整解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | SetEnvIf Origin"http(s)?://(.+\.)?domain\.com(:\d{1,5})?$" CRS=$0 Header always set Access-Control-Allow-Origin"%{CRS}e" env=CRS Header merge Vary"Origin" Header always set Access-Control-Allow-Methods"GET, POST" Header always set Access-Control-Allow-Headers: * # Cached for a day Header always set Access-Control-Max-Age: 86400 RewriteEngine On # Respond with 200OK for OPTIONS RewriteCond %{REQUEST_METHOD} OPTIONS RewriteRule ^(.*)$ $1 [R=200,L] |
当从"cookie域"(www.domain.tld)读取字体时,我们在静态"无cookie"域上遇到与Font Awesome类似的问题,这篇文章是我们的英雄。请参阅此处:如何修复"缺少跨域资源共享(CORS)响应标头"webfont问题?
对于copy / paste-r类型(以及提供一些道具),我将所有贡献拼凑在一起,并将其添加到站点根目录的.htaccess文件的顶部:
1 2 3 4 5 6 7 | <IfModule mod_headers.c> <IfModule mod_rewrite.c> SetEnvIf Origin"http(s)?://(.+\.)?(othersite\.com|mywebsite\.com)(:\d{1,5})?$" CORS=$0 Header set Access-Control-Allow-Origin"%{CORS}e" env=CORS Header merge Vary"Origin" </IfModule> </IfModule> |
超级安全,超级优雅。喜欢它:您不必为资源窃贼/热链接类型打开服务器带宽。
道具:@Noyo @DaveRandom @ pratap-koritala
(我试着将此作为对已接受答案的评论,但我还不能这样做)
对于Spring Boot,我发现这个
我不得不修改Lars的答案,因为孤立的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | function getCORSHeaderOrigin($allowed, $input) { if ($allowed == '*') { return '*'; } if (!is_array($allowed)) { $allowed = array($allowed); } foreach ($allowed as &$value) { $value = preg_quote($value, '/'); if (($wildcardPos = strpos($value, '\*')) !== false) { $value = str_replace('\*', '(.*)', $value); } } $regexp = '/^(' . implode('|', $allowed) . ')$/'; $inputHost = parse_url($input, PHP_URL_HOST); if ($inputHost === null || !preg_match($regexp, $inputHost, $matches)) { return 'none'; } return $input; } |
用法如下:
1 2 3 | if (isset($_SERVER['HTTP_ORIGIN'])) { header("Access-Control-Allow-Origin:" . getCORSHeaderOrigin(array("*.myproduction.com","localhost"), $_SERVER['HTTP_ORIGIN'])); } |
看起来原来的答案是针对Apache 2.4之前的。它对我不起作用。这是我必须改变以使其在2.4中工作的内容。这适用于yourcompany.com的任何深度子域。
1 2 3 | SetEnvIf Host ^((?:.+\.)*yourcompany\.com?)$ CORS_ALLOW_ORIGIN=$1 Header append Access-Control-Allow-Origin %{REQUEST_SCHEME}e://%{CORS_ALLOW_ORIGIN}e env=CORS_ALLOW_ORIGIN Header merge Vary"Origin" |
Lars回答略有变化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <?php function validateOrigin($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; } // CORS Preflight if($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { header('Access-Control-Allow-Origin: '.validateOrigin('https://*.domain.com', $_SERVER['HTTP_ORIGIN'])); header('X-Response-Code: 204', true, 204); header('Access-Control-Allow-Methods: GET'); header('Access-Control-Allow-Headers: Content-Type'); } elseif($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['VARIABLEHERE']) ) { // CODE HERE if($info["http_code"] === 200){ // 200 OK header('Content-type: application/json; charset=utf-8'); // CORS headers header('Access-Control-Allow-Origin: '.validateOrigin('https://*.domain.com', $_SERVER['HTTP_ORIGIN'])); echo $response; } else { header('X-Service-Response-Code: '.$info["http_code"], true, $info["http_code"]); } } else { // 400 bad request. header('X-Response-Code: 400', true, 400); } exit; |