Subdomain, port, dan protokol wildcard Access-Control-Allow-Origin

312

Saya mencoba mengaktifkan CORS untuk semua subdomain, port, dan protokol.

Misalnya, saya ingin dapat menjalankan permintaan XHR dari http://sub.mywebsite.com:8080/ ke https://www.mywebsite.com/ *

Biasanya, saya ingin mengaktifkan permintaan dari pencocokan asal (dan terbatas pada):

//*.mywebsite.com:*/*

Elie
sumber

Jawaban:

207

Berdasarkan jawaban DaveRandom , saya juga bermain-main dan menemukan solusi Apache yang sedikit lebih sederhana yang menghasilkan hasil yang sama ( Access-Control-Allow-Origindiatur ke protokol + domain + port saat ini secara dinamis) tanpa menggunakan aturan penulisan ulang:

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"

Dan itu saja.

Mereka yang ingin mengaktifkan CORS di domain induk (mis. Mywebsite.com) di samping semua subdomainnya dapat dengan mudah mengganti ekspresi reguler di baris pertama dengan yang ini:

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

Catatan: Untuk kepatuhan spesifikasi dan perilaku cache yang benar, SELALU menambahkan Vary: Origintajuk respons untuk sumber daya yang mendukung CORS, bahkan untuk permintaan non-CORS dan yang dari asal yang tidak diizinkan (lihat contoh mengapa ).

Noyo
sumber
1
Kami memiliki ini hampir (bukan Bervariasi Asal) dan mendapat perilaku buruk ketika pengunjung melompat antara beberapa sub domain menggunakan font yang sama. Font dan header akses-kontrol-asal juga di-cache. Saya telah membuat sedikit perubahan pada yang ini: Saya menggunakan "Access-Control-Allow-Origin *" jika permintaan tersebut dari salah satu domain yang diizinkan. Mungkin ini diselesaikan dengan "Vary Origin" yang sebelumnya tidak kita miliki ... sekarang menambahkan itu juga.
Erik Melkersson
2
Tidak berfungsi untuk domain utama 'mywebsite.com'
biology.info
1
@pgmann, tidak perlu keluar dari //konteks ini, karena conf Apache tidak menggunakan ekspresi reguler terbatas-slash. Regexr mengeluh karena, dalam konteks itu, garis miring memiliki arti khusus sebagai pembatas.
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.
Aero Wang
3
Di mana Anda menempatkan kode ini? .htaccess atau dalam konfigurasi host virtual apache?
Glen
252

Spesifikasi CORS semuanya atau tidak sama sekali. Ini hanya mendukung *, nullatau protokol + domain + port yang tepat: http://www.w3.org/TR/cors/#access-control-allow-origin-response-header

Server Anda perlu memvalidasi header asal menggunakan regex, dan kemudian Anda bisa menggemakan nilai asal di header respons Access-Control-Allow-Origin.

monsur
sumber
7
@Dexter "null" dapat digunakan sebagai respons terhadap asal "null", misalnya saat membuat permintaan CORS dari file: // skema.
monsur
130
Sangat picik bahwa spesifikasi CORS tidak akan mendukung kasus penggunaan OP yang sebenarnya .
Agustus
6
@aroth: Tidak juga, spec memungkinkan implementasi untuk menggunakan setiap sintaks pencocokan mereka inginkan; itu hanya akan menjadi pandangan picik untuk implementasi yang tidak mendukung kasus penggunaan ini. Dengan kata lain, apa yang Anda tentukan ke server Anda bukan nilai ACAO, yang terakhir hanyalah detail protokol. Asumsi saya adalah bahwa ada skenario keamanan yang memerlukan atau manfaat dari asal digemakan kembali, tetapi implementasi yang naif bekerja dengan hanya mengatakan "OK" atau tidak.
mentega
3
Pembaruan dari 2015: Jawaban ini harus dianggap berbahaya, karena tidak lengkap dan dapat menyebabkan masalah caching. Silakan lihat jawaban saya di bawah ini untuk implementasi yang tepat (untuk Apache) dan penjelasan: stackoverflow.com/a/27990162/357774 . Juga, @aroth, seperti mentega menunjukkan, spec sebenarnya tidak memungkinkan penggunaan kasus yang sebenarnya OP: w3.org/TR/cors/#resource-implementation . Dan sebagaimana jawaban ini tunjukkan, terserah server untuk mengimplementasikannya. Ini dapat dilakukan dalam 3 baris, seperti terlihat pada jawaban yang disebutkan di atas.
Noyo
19
@Noyo - Saya akan mengklarifikasi makna asli saya. Ini sangat picik bahwa spesifikasi CORS tidak sepenuhnya mengharuskan semua server yang mengimplementasikan CORS untuk menyediakan dukungan otomatis dan built-in untuk kasus penggunaan OP yang tepat . Membiarkannya hingga setiap pengguna individu untuk membangun shim mereka sendiri menggunakan kode PHP khusus, aturan penulisan ulang, atau apa pun yang Anda miliki adalah resep untuk fragmentasi, bug, dan bencana. Server devs harus tahu lebih baik dari itu; dan jika tidak, spesifikasi CORS harus memaksa mereka.
aroth
59

EDIT : Gunakan solusi @ Noyo bukan yang ini. Ini lebih sederhana, lebih jelas dan kemungkinan jauh lebih banyak performan di bawah beban.

KIRI JAWABAN ASLI DI SINI UNTUK TUJUAN SEJARAH SAJA !!


Saya melakukan beberapa masalah dengan masalah ini dan muncul dengan solusi .htaccess (atau httpd.conf) yang dapat digunakan kembali ini yang bekerja dengan 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>

Cukup setel ACCESS_CONTROL_ROOTvariabel di bagian atas blok ke domain root Anda dan itu akan menggemakan nilai Origin:header permintaan kembali ke klien dalam nilai Access-Control-Allow-Origin:header respons jika cocok dengan domain Anda.

Perhatikan juga bahwa Anda dapat menggunakan sub.mydomain.comsebagai ACCESS_CONTROL_ROOTdan itu akan membatasi asal sub.mydomain.comdan *.sub.mydomain.com(yaitu tidak harus menjadi root domain). Elemen-elemen yang diizinkan bervariasi (protokol, port) dapat dikontrol dengan memodifikasi bagian pencocokan URI dari regex.

DaveRandom
sumber
22

Saya menjawab pertanyaan ini, karena jawaban yang diterima tidak dapat mengikuti

  1. pengelompokan regex adalah hit kinerja , yang tidak perlu.
  2. tidak dapat mencocokkan domain primer dan hanya berfungsi untuk sub domain.

Misalnya: Ia tidak akan mengirim header CORS untuk http://mywebsite.com, sementara berfungsi untuk 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"

Untuk mengaktifkan situs Anda, Anda cukup meletakkan situs Anda di tempat "mywebsite.com" di Konfigurasi Apache di atas.

Untuk mengizinkan Beberapa situs:

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

Pengujian Setelah penggelaran:

Respons ikal berikut harus memiliki tajuk "Access-Control-Allow-Origin" setelah perubahan.

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

Saya membutuhkan solusi PHP saja, jadi kalau-kalau ada yang membutuhkannya juga. Dibutuhkan string input yang diizinkan seperti "* .example.com" dan mengembalikan nama server header permintaan, jika inputnya cocok.

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

Dan berikut ini adalah kasus uji untuk penyedia data 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'),
Lars
sumber
1
+1, diedit untuk digunakan preg_quote()karena itulah cara yang benar untuk melakukannya (meskipun .satu-satunya meta char regexp valid dalam nama DNS, preg_quote()menjelaskan operasi yang dimaksud lebih baik)
DaveRandom
1
Harus diklarifikasi bahwa nonebukan nilai semantik yang valid untuk header (atau setidaknya, tidak melakukan apa yang disiratkan) sesuai dengan spesifikasi. Dengan demikian, return null;mungkin lebih masuk akal untuk cabang itu, dan dalam hal itu tidak ada header yang harus dikirim ke klien, sehingga harus diperiksa oleh pemanggil.
DaveRandom
preg_quote()akan mengutip tanda * dan jadi str_replace()misalnya meninggalkan "y" yatim piatu.
Christoffer Bubach
1
ini berguna, saya menghabiskan waktu pada masalah CORS sampai saya menyadari situs saya memiliki "www" di ajax, tetapi tidak dalam struktur permalink - solusi Anda membantu saya memahami di mana masalah itu dan menyelesaikannya untuk saya.
Sol
3

Saat mengatur Access-Control-Allow-Origindalam .htaccess, hanya yang berikut yang berfungsi:

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

Aku mencoba beberapa kata kunci yang disarankan lainnya Header append, Header set, tidak bekerja seperti yang disarankan di banyak jawaban pada SO, meskipun saya tidak tahu apakah kata kunci tersebut sudah ketinggalan jaman atau tidak berlaku untuk nginx .

Inilah solusi lengkap saya:

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]
AamirR
sumber
2

Kami mengalami masalah serupa dengan Font Awesome pada domain "cookie-kurang" statis ketika membaca font dari "domain cookie" (www.domain.tld) ​​dan posting ini adalah pahlawan kami. Lihat di sini: Bagaimana saya dapat memperbaiki masalah webfont 'Header Berbagi Sumber Daya Hilang (CORS) yang Hilang?

Untuk tipe salin / tempel-r (dan untuk memberikan beberapa alat peraga) saya mengumpulkan ini bersama-sama dari semua kontribusi dan menambahkannya ke bagian atas file .htaccess dari root situs:

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

Super Aman, Super Elegan. Senang: Anda tidak perlu membuka bandwidth server Anda untuk sumber daya jenis pencuri / hot-link-er.

Props untuk: @Noyo @DaveRandom @ pratap-koritala

(Saya mencoba meninggalkan ini sebagai komentar untuk jawaban yang diterima, tetapi saya belum bisa melakukannya)

kanidrive
sumber
0

Sepertinya jawaban asli untuk pra Apache 2.4. Itu tidak berhasil untuk saya. Inilah yang harus saya ubah untuk membuatnya bekerja di 2.4. Ini akan berfungsi untuk kedalaman subdomain dari perusahaanAnda .

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"
Jack K.
sumber
0

Saya harus memodifikasi jawaban Lars sedikit, karena seorang yatim \berakhir di regex, untuk hanya membandingkan host yang sebenarnya (tidak memperhatikan protokol atau port) dan saya ingin mendukung localhostdomain selain domain produksi saya. Jadi saya mengubah $allowedparameter menjadi array.

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

Penggunaan sebagai berikut:

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

dalam kasus saya menggunakan sudut

di interseptor HTTP saya, saya atur

with Credentials: true.

di tajuk permintaan

Hosam Hemaily
sumber