Các tên miền, cổng và giao thức ký tự đại diện Access-Control-Allow-Origin


312

Tôi đang cố gắng kích hoạt CORS cho tất cả các tên miền phụ, cổng và giao thức.

Ví dụ: tôi muốn có thể chạy yêu cầu XHR từ http://sub.mywebsite.com:8080/ đến https://www.mywebsite.com/ *

Thông thường, tôi muốn kích hoạt yêu cầu từ nguồn gốc khớp (và giới hạn):

//*.mywebsite.com:*/*

Câu trả lời:


207

Dựa trên câu trả lời của DaveRandom , tôi cũng đã chơi xung quanh và tìm thấy một giải pháp Apache đơn giản hơn một chút tạo ra kết quả tương tự ( Access-Control-Allow-Originđược đặt thành giao thức cụ thể hiện tại + domain + port) mà không sử dụng bất kỳ quy tắc viết lại nào:

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"

Và đó là nó.

Những người muốn kích hoạt CORS trên tên miền mẹ (ví dụ mywebsite.com) ngoài tất cả các tên miền phụ của nó có thể chỉ cần thay thế biểu thức thông thường trong dòng đầu tiên bằng dòng này:

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

Lưu ý: Để tuân thủ thông số kỹ thuật và hành vi bộ đệm chính xác, LUÔN LUÔN thêm Vary: Origintiêu đề phản hồi cho các tài nguyên hỗ trợ CORS, ngay cả đối với các yêu cầu không phải CORS và các yêu cầu từ nguồn gốc không được phép (xem ví dụ tại sao ).


1
Chúng tôi đã có gần như điều này (không phải Vary Origin) và có hành vi xấu khi khách truy cập nhảy giữa nhiều tên miền phụ sử dụng cùng một phông chữ. Phông chữ và tiêu đề nguồn gốc kiểm soát truy cập cũng được lưu trữ. Tôi đã thực hiện một thay đổi nhỏ trên cái này: Tôi sử dụng "Kiểm soát truy cập-Cho phép-Xuất xứ *" nếu yêu cầu đến từ một trong những miền được phép của chúng tôi. Có lẽ điều này đã được giải quyết với "Nguồn gốc khác nhau" mà trước đây chúng ta không có ... bây giờ cũng đã thêm điều đó.
Erik Melkersson

2
Không hoạt động cho tên miền chính 'mywebsite.com'
biology.info

1
@pgmann, không cần phải thoát khỏi //bối cảnh này, vì conf Apache không sử dụng các biểu thức chính quy được phân cách bằng dấu gạch chéo. Regexr phàn nàn bởi vì, trong bối cảnh đó, dấu gạch chéo có ý nghĩa đặc biệt là dấu phân cách.
Noyo

1
The 'Access-Control-Allow-Origin' header contains multiple values '^(https?://(?:.+.)?aerofotea.com(?::d{1,5})?)$', but only one is allowed. Origin 'http://local.aerofotea.com' is therefore not allowed access.
Hàng không Wang Wang

3
Nơi nào bạn đặt mã này? .htaccess hoặc trong cấu hình máy chủ ảo apache?
Glen

252

Thông số kỹ thuật CORS là tất cả hoặc không có gì. Nó chỉ hỗ trợ *, nullhoặc chính xác giao thức + miền + cổng: http://www.w3.org/TR/cors/#access-control-allow-origin-response-header

Máy chủ của bạn sẽ cần xác thực tiêu đề gốc bằng regex và sau đó bạn có thể lặp lại giá trị gốc trong tiêu đề phản hồi Access-Control-Allow-Origin.


7
@Dexter "null" có thể được sử dụng để đáp ứng với nguồn gốc "null", ví dụ: khi thực hiện yêu cầu CORS từ tệp: // lược đồ.
ngày

130
Thật vô cùng thiển cận rằng thông số CORS sẽ không hỗ trợ trường hợp sử dụng chính xác của OP .
aroth

6
@aroth: Không thực sự, thông số kỹ thuật cho phép triển khai sử dụng bất kỳ cú pháp phù hợp nào họ muốn; nó sẽ chỉ bị cận thị sâu sắc đối với việc triển khai không hỗ trợ cho trường hợp sử dụng này. Nói cách khác, những gì bạn chỉ định cho máy chủ của mình không phải là giá trị ACAO, cái sau chỉ là một chi tiết giao thức. Giả định của tôi là có một kịch bản bảo mật đòi hỏi hoặc lợi ích từ nguồn gốc được lặp lại, nhưng một triển khai ngây thơ hoạt động bằng cách chỉ nói "OK" hoặc không.
tne

3
Cập nhật từ năm 2015: Câu trả lời này nên được coi là có hại, vì nó không đầy đủ và có thể dẫn đến các vấn đề về bộ đệm. Vui lòng xem câu trả lời của tôi dưới đây để biết cách triển khai thích hợp (đối với Apache) và giải thích: stackoverflow.com/a/27990162/357774 . Ngoài ra, @aroth, như tne chỉ ra, spec thực sự không cho phép trường hợp sử dụng chính xác của OP: w3.org/TR/cors/#resource-implementation . Và như câu trả lời này chỉ ra, tùy thuộc vào máy chủ để thực hiện nó. Điều này có thể được thực hiện trong 3 dòng, như đã thấy trong câu trả lời được đề cập ở trên.
Noyo

19
@Noyo - Tôi sẽ làm rõ nghĩa gốc của tôi sau đó. Thật sự rất thiển cận rằng thông số CORS không yêu cầu nghiêm ngặt tất cả các máy chủ triển khai CORS để cung cấp hỗ trợ tự động, tích hợp cho trường hợp sử dụng chính xác của OP . Để cho mỗi người dùng cá nhân xây dựng shim riêng của họ bằng cách sử dụng mã PHP tùy chỉnh, viết lại quy tắc hoặc những gì bạn có là một công thức để phân mảnh, lỗi và thảm họa. Các nhà phát triển máy chủ nên biết rõ hơn thế; và nếu họ không, thông số CORS nên buộc họ phải.
aroth

59

EDIT : Sử dụng giải pháp @ Noyo thay vì giải pháp này. Nó đơn giản hơn, rõ ràng hơn và có khả năng thực hiện nhiều hơn khi tải.

CÂU TRẢ LỜI GỐC TẠI ĐÂY CHỈ DÀNH CHO MỤC ĐÍCH LỊCH SỬ !!


Tôi đã giải quyết vấn đề này và đưa ra giải pháp .htaccess (hoặc httpd.conf) có thể sử dụng lại này hoạt động với Apache:

<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>

Chỉ cần đặt ACCESS_CONTROL_ROOTbiến ở đầu khối cho miền gốc của bạn và nó sẽ Origin:trả lại giá trị tiêu đề yêu cầu trở lại máy khách trong Access-Control-Allow-Origin:giá trị tiêu đề phản hồi nếu nó khớp với miền của bạn.

Cũng lưu ý rằng bạn có thể sử dụng sub.mydomain.comlàm ACCESS_CONTROL_ROOTvà nó sẽ giới hạn nguồn gốc sub.mydomain.com*.sub.mydomain.com(nghĩa là nó không phải là gốc miền). Các yếu tố được phép thay đổi (giao thức, cổng) có thể được kiểm soát bằng cách sửa đổi phần khớp URI của biểu thức chính quy.


22

Tôi đang trả lời câu hỏi này, vì câu trả lời được chấp nhận không thể làm theo

  1. nhóm regex là một hit hiệu suất , không cần thiết.
  2. không thể phù hợp với tên miền chính và nó chỉ hoạt động cho tên miền phụ.

Ví dụ: Nó sẽ không gửi tiêu đề CORS cho http://mywebsite.com trong khi hoạt động cho http://somedomain.mywebsite.com/

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"

Để kích hoạt trang web của bạn, bạn chỉ cần đặt trang web của mình thay cho "mywebsite.com" trong Cấu hình Apache ở trên.

Để cho phép nhiều trang web:

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

Kiểm tra sau khi triển khai:

Phản hồi curl sau đây sẽ có tiêu đề "Access-Control-Allow-Origin" sau khi thay đổi.

curl -X GET -H "Origin: http://examplesite1.com" --verbose http://examplesite2.com/query

12

Tôi cần một giải pháp chỉ dành cho PHP, vì vậy trong trường hợp ai đó cũng cần nó. Nó nhận một chuỗi đầu vào được phép như "* .example.com" và trả về tên máy chủ tiêu đề yêu cầu, nếu đầu vào khớp.

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;
}

Và đây là các trường hợp thử nghiệm cho nhà cung cấp dữ liệu 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'),

1
+1, chỉnh sửa để sử dụng preg_quote()vì đó là cách chính xác để làm điều đó (mặc dù .là chỉ regexp meta char hợp lệ trong một tên DNS, preg_quote()mô tả các hoạt động dự định tốt hơn)
DaveRandom

1
Cần làm rõ rằng đó nonekhông phải là một giá trị hợp lệ về mặt ngữ nghĩa cho tiêu đề (hoặc ít nhất, không làm những gì nó ngụ ý) theo thông số kỹ thuật. Như vậy, return null;có thể có ý nghĩa hơn đối với nhánh đó và trong trường hợp đó, không có tiêu đề nào được gửi đến máy khách, do đó, nó sẽ được người gọi kiểm tra.
DaveRandom

preg_quote()sẽ trích dẫn dấu * và vì vậy, str_replace()ví dụ để lại một "\" mồ côi.
Christoffer Bubach

1
Điều này rất hữu ích, tôi đã dành thời gian cho vấn đề CORS cho đến khi tôi nhận ra trang web của mình có "www" trong ajax, nhưng không phải trong cấu trúc permalink - giải pháp của bạn đã giúp tôi hiểu vấn đề ở đâu và giải quyết vấn đề đó cho tôi.
Sol

3

Khi cài đặt Access-Control-Allow-Origintrong .htaccess, chỉ hoạt động sau:

SetEnvIf Origin "http(s)?://(.+\.)?domain\.com(:\d{1,5})?$" CRS=$0
Header always set Access-Control-Allow-Origin "%{CRS}e" env=CRS

Tôi đã thử nhiều từ khóa gợi ý khác Header append, Header set, không ai làm việc như đề xuất trong nhiều câu trả lời trên SO, mặc dù tôi không có ý tưởng nếu những từ khóa được lỗi thời hoặc không hợp lệ cho nginx .

Đây là giải pháp hoàn chỉnh của tôi:

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]

2

Chúng tôi đã gặp vấn đề tương tự với Font Awesome trên miền "không có cookie" tĩnh khi đọc phông chữ từ "tên miền cookie" (www.domain.tld) ​​và bài đăng này là anh hùng của chúng tôi. Xem tại đây: Làm cách nào tôi có thể khắc phục sự cố webfont 'Thiếu tiêu đề chia sẻ tài nguyên chéo (CORS)'?

Đối với các loại sao chép / dán-r (và để cung cấp một số đạo cụ), tôi đã ghép nó lại với nhau từ tất cả các đóng góp và thêm nó vào đầu tệp .htaccess của trang gốc:

<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>

Siêu an toàn, siêu thanh lịch. Thích nó: Bạn không cần phải mở băng thông máy chủ của mình cho các loại kẻ trộm tài nguyên / các loại hot-link-er.

Đạo cụ cho: @Noyo @DaveRandom @ pratap-koritala

(Tôi đã cố gắng để lại nhận xét này như một nhận xét cho câu trả lời được chấp nhận, nhưng tôi chưa thể làm điều đó)



0

Có vẻ như câu trả lời ban đầu là dành cho Apache 2.4. Nó không làm việc cho tôi. Đây là những gì tôi đã phải thay đổi để làm cho nó hoạt động trong 2.4. Điều này sẽ làm việc cho bất kỳ độ sâu của tên miền phụ của yourcompany.com .

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"

0

Tôi đã phải sửa đổi câu trả lời của Lars một chút, vì một đứa trẻ mồ côi \đã kết thúc trong regex, để chỉ so sánh máy chủ thực tế (không chú ý đến giao thức hoặc cổng) và tôi muốn hỗ trợ localhosttên miền bên cạnh miền sản xuất của mình. Do đó tôi đã thay đổi $allowedtham số thành một mảng.

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;
}

Cách sử dụng như sau:

if (isset($_SERVER['HTTP_ORIGIN'])) {
    header("Access-Control-Allow-Origin: " . getCORSHeaderOrigin(array("*.myproduction.com", "localhost"), $_SERVER['HTTP_ORIGIN']));
}

0

trong trường hợp của tôi sử dụng góc

trong bộ chặn HTTP của tôi, tôi đã đặt

with Credentials: true.

trong tiêu đề của yêu cầu

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.