การเข้าถึง - การควบคุม - อนุญาต - กำเนิดโดเมนย่อยไวด์การ์ด, พอร์ตและโปรโตคอล


312

ฉันพยายามเปิดใช้งาน CORS สำหรับโดเมนย่อยพอร์ตและโปรโตคอลทั้งหมด

ตัวอย่างเช่นฉันต้องการให้สามารถเรียกใช้คำขอ XHR จากhttp://sub.mywebsite.com:8080/ไปยังhttps://www.mywebsite.com/ *

โดยทั่วไปฉันต้องการเปิดใช้งานคำขอจากการจับคู่ต้นกำเนิด (และ จำกัด เฉพาะ):

//*.mywebsite.com:*/*

คำตอบ:


207

จากคำตอบของ 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"

และนั่นคือมัน

ผู้ที่ต้องการเปิดใช้งาน CORS ในโดเมนหลัก (เช่น mywebsite.com) นอกเหนือจากโดเมนย่อยทั้งหมดสามารถแทนที่นิพจน์ปกติในบรรทัดแรกด้วยบรรทัดนี้:

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

หมายเหตุ: สำหรับการปฏิบัติตามข้อกำหนดและการแคชที่ถูกต้อง ALWAYS จะเพิ่มVary: Originหัวข้อการตอบกลับสำหรับทรัพยากรที่เปิดใช้งาน CORS แม้สำหรับคำขอที่ไม่ใช่ CORS และจากแหล่งที่ไม่อนุญาต (ดูตัวอย่างสาเหตุ )


1
เรามีสิ่งนี้เกือบจะ (ไม่แตกต่างกันไป) และมีพฤติกรรมที่ไม่ดีเมื่อผู้เข้าชมข้ามระหว่างโดเมนย่อยต่างๆโดยใช้แบบอักษรเดียวกัน แบบอักษรและส่วนหัวของ access-control-origin ก็ถูกแคชไว้เช่นกัน ฉันได้ทำการเปลี่ยนแปลงเล็กน้อยในเรื่องนี้: ฉันใช้ "Access-Control-Allow-Origin *" ถ้าคำขอมาจากหนึ่งในโดเมนที่อนุญาตของเรา บางทีนี่อาจจะถูกแก้ไขด้วย "Vary Origin" ที่เราไม่เคยมีมาก่อน ... ตอนนี้ก็เพิ่มเข้าไปเช่นกัน
Erik Melkersson

2
ใช้งานไม่ได้กับโดเมนหลัก 'mywebsite.com'
biology.info

1
@pgmann ไม่จำเป็นต้องหลบเลี่ยง//ในบริบทนี้เนื่องจาก Apache conf ไม่ได้ใช้การแสดงออกปกติคั่นด้วยเครื่องหมายทับ Regexr บ่นเพราะในบริบทนั้นเครื่องหมายทับมีความหมายพิเศษเป็นตัวคั่น
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
คุณวางรหัสนี้ที่ไหน . htaccess หรือในการกำหนดค่าโฮสต์เสมือน apache หรือไม่
เกลน

252

ข้อมูลจำเพาะ CORS เป็นทั้งหมดหรือไม่มีอะไรเลย จะสนับสนุนเฉพาะ*, nullหรือที่แน่นอนโดเมนโปรโตคอล + พอร์ต: http://www.w3.org/TR/cors/#access-control-allow-origin-response-header

เซิร์ฟเวอร์ของคุณจะต้องตรวจสอบความถูกต้องของส่วนหัวกำเนิดโดยใช้ regex จากนั้นคุณสามารถสะท้อนค่าเริ่มต้นในส่วนหัวของการตอบกลับ Access-Control-Allow-Origin


7
@Dexter "null" สามารถใช้ในการตอบสนองต่อต้นกำเนิด "null" เช่นเมื่อทำการร้องขอ CORS จากไฟล์: // scheme
นาย

130
มันเป็นสิ่งที่สั้นอย่างยิ่งที่ CORS spec จะไม่รองรับกรณีใช้งานที่แน่นอนของ OP
aroth

6
@aroth: ไม่ได้จริงๆสเปคจะช่วยให้การใช้งานที่จะใช้ใด ๆไวยากรณ์การจับคู่ที่พวกเขาต้องการ; มันจะสั้นอย่างลึกซึ้งสำหรับการดำเนินการไม่สนับสนุนกรณีการใช้งานนี้ กล่าวอีกนัยหนึ่งสิ่งที่คุณระบุให้เซิร์ฟเวอร์ของคุณไม่ใช่ค่า ACAO ส่วนหลังเป็นเพียงรายละเอียดโปรโตคอล ข้อสันนิษฐานของฉันคือมีสถานการณ์ความปลอดภัยที่ต้องใช้หรือได้รับประโยชน์จากแหล่งกำเนิดที่สะท้อนกลับมา
tne

3
อัปเดตจาก 2015: คำตอบนี้ควรถือว่าเป็นอันตรายเนื่องจากไม่สมบูรณ์และอาจนำไปสู่ปัญหาการแคช โปรดดูคำตอบของฉันด้านล่างสำหรับการดำเนินงานที่เหมาะสม (สำหรับ Apache) และคำอธิบาย: stackoverflow.com/a/27990162/357774 นอกจากนี้ @aroth เป็น TNE ชี้ให้เห็นข้อมูลจำเพาะจริงไม่อนุญาตให้ใช้กรณีของ OP ที่แน่นอน: w3.org/TR/cors/#resource-implementation และเนื่องจากคำตอบนี้ชี้ให้เห็นว่ามันขึ้นอยู่กับเซิร์ฟเวอร์ที่จะติดตั้ง สามารถทำได้ใน 3 บรรทัดดังที่เห็นในคำตอบที่อ้างถึงข้างต้น
Noyo

19
@Noyo - ฉันจะอธิบายความหมายดั้งเดิมของฉันแล้ว เป็นเรื่องที่สั้นมากที่ CORS spec ไม่ต้องการเซิร์ฟเวอร์ทั้งหมดที่ใช้ CORS อย่างเคร่งครัดเพื่อให้การสนับสนุนอัตโนมัติในตัวสำหรับกรณีการใช้งานจริงของ OP ปล่อยให้ผู้ใช้แต่ละคนสร้าง shim ของตนเองโดยใช้โค้ด PHP ที่กำหนดเองกฎการเขียนซ้ำหรือสิ่งที่คุณมีเป็นสูตรสำหรับการแยกส่วนข้อบกพร่องและภัยพิบัติ ผู้พัฒนาเซิร์ฟเวอร์ควรรู้ดีกว่านั้น; และถ้าไม่เช่นนั้นข้อมูลจำเพาะ CORS ควรบังคับให้พวกเขาทำ
58

59

แก้ไข : ใช้โซลูชันของ @ Noyoแทนอันนี้ มันง่ายกว่าชัดเจนขึ้นและมีแนวโน้มที่จะมีประสิทธิภาพมากขึ้นภายใต้ภาระ

ต้นฉบับคำสั่งซ้ายที่นี่เพื่อวัตถุประสงค์ทางประวัติศาสตร์เท่านั้น !!


ฉันได้ลองเล่นกับปัญหานี้และพบกับโซลูชัน. htaccess (หรือ httpd.conf) ที่ใช้ซ้ำได้ซึ่งทำงานร่วมกับ 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>

เพียงตั้งค่าACCESS_CONTROL_ROOTตัวแปรที่ด้านบนของบล็อกเป็นโดเมนรูทของคุณและมันจะสะท้อนOrigin:ค่าส่วนหัวของคำขอกลับไปที่ลูกค้าในAccess-Control-Allow-Origin:ค่าส่วนหัวการตอบสนองหากตรงกับโดเมนของคุณ

โปรดทราบว่าคุณสามารถใช้sub.mydomain.comเป็นACCESS_CONTROL_ROOTและจะ จำกัด การกำเนิดsub.mydomain.comและ*.sub.mydomain.com(เช่นไม่จำเป็นต้องเป็นรูทโดเมน) องค์ประกอบที่ได้รับอนุญาตให้เปลี่ยนแปลง (โปรโตคอล, พอร์ต) สามารถควบคุมได้โดยการปรับเปลี่ยนส่วนการจับคู่ URI ของ regex


22

ฉันกำลังตอบคำถามนี้เพราะคำตอบที่ยอมรับไม่สามารถทำตามได้

  1. การจัดกลุ่ม regex เป็นผลการดำเนินงานที่ไม่จำเป็น
  2. ไม่สามารถจับคู่โดเมนหลักและใช้ได้กับโดเมนย่อยเท่านั้น

ตัวอย่างเช่น: จะไม่ส่งส่วนหัว CORS สำหรับhttp://mywebsite.comในขณะที่ทำงานกับ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"

หากต้องการเปิดใช้งานสำหรับไซต์ของคุณคุณเพียงวางเว็บไซต์ของคุณไว้ใน "mywebsite.com" ในการกำหนดค่า Apache ด้านบน

วิธีอนุญาตเว็บไซต์หลายแห่ง:

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

การทดสอบหลังจากปรับใช้:

การตอบสนองของ curl ต่อไปนี้ควรมีส่วนหัว "Access-Control-Allow-Origin" หลังจากการเปลี่ยนแปลง

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

12

ฉันต้องการโซลูชัน 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;
}

และนี่คือกรณีทดสอบสำหรับผู้ให้บริการข้อมูล 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 แก้ไขเพื่อการใช้งานpreg_quote()เพราะนั่นคือวิธีที่ถูกต้องที่จะทำมัน (แม้ว่า.เป็นเพียง regexp meta ถ่านที่ถูกต้องในชื่อ DNS ที่preg_quote()อธิบายการดำเนินการตั้งใจที่ดีกว่า)
DaveRandom

1
ควรชี้แจงว่าnoneไม่ใช่ค่าที่ถูกต้องเชิงความหมายสำหรับส่วนหัว (หรืออย่างน้อยไม่ได้ทำในสิ่งที่มันหมายถึง) ตามข้อมูลจำเพาะ เช่นนี้return null;อาจเหมาะสมกว่าสำหรับสาขานั้นและในกรณีนั้นไม่ควรส่งส่วนหัวไปยังไคลเอนต์ดังนั้นผู้โทรควรได้รับการตรวจสอบ
DaveRandom

preg_quote()จะอ้างอิงเครื่องหมาย * และstr_replace()ตัวอย่างเช่นจะทิ้งคำว่า "\" ไว้
Christoffer Bubach

1
สิ่งนี้มีประโยชน์ฉันใช้เวลากับปัญหา CORS จนกระทั่งฉันรู้ว่าไซต์ของฉันมี "www" ในอาแจ็กซ์ แต่ไม่ได้อยู่ในโครงสร้างลิงก์ถาวร - โซลูชันของคุณช่วยให้ฉันเข้าใจว่าปัญหานั้นอยู่ที่ไหนและแก้ไขให้ฉันด้วย
โซล

3

เมื่อตั้งค่าAccess-Control-Allow-Originใน. htaccess ทำงานต่อไปนี้เท่านั้น:

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

ฉันพยายามหลายคำหลักที่แนะนำอื่น ๆHeader append, Header set, ไม่มีใครทำงานเป็นปัญหาในหลายคำตอบในดังนั้นแม้ว่าฉันมีความคิดว่าคำหลักเหล่านี้จะล้าสมัยหรือไม่ถูกต้องสำหรับNginx

นี่คือทางออกที่สมบูรณ์ของฉัน:

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

เราพบปัญหาที่คล้ายกันกับ Font Awesome ในโดเมน "คุกกี้น้อย" แบบคงที่เมื่ออ่านแบบอักษรจาก "โดเมนคุกกี้" (www.domain.tld) ​​และโพสต์นี้เป็นฮีโร่ของเรา ดูที่นี่: ฉันจะแก้ไขปัญหาการตอบกลับของส่วนหัว 'Missing Cross-Origin Resource Sharing (CORS)' ได้อย่างไร

สำหรับประเภท copy / paste-r (และเพื่อมอบอุปกรณ์ประกอบฉาก) ฉันได้รวบรวมสิ่งเหล่านี้จากการมีส่วนร่วมทั้งหมดและเพิ่มไว้ที่ด้านบนของไฟล์. htaccess ของรูทไซต์:

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

ปลอดภัยสุด ๆ Elegant สุด ๆ ชอบมาก: คุณไม่ต้องเปิดแบนด์วิธเซิร์ฟเวอร์ของคุณเพื่อขโมยทรัพยากร / ประเภท hot-link-er

อุปกรณ์ประกอบฉากถึง: @Noyo @DaveRandom @ pratap-koritala

(ฉันพยายามจะฝากความคิดเห็นไว้ในคำตอบที่ยอมรับ แต่ฉันยังทำไม่ได้)



0

ดูเหมือนว่าคำตอบเดิมสำหรับ Apache 2.4 ก่อน มันไม่ได้ผลสำหรับฉัน นี่คือสิ่งที่ฉันต้องเปลี่ยนเพื่อให้ทำงานได้ใน 2.4 นี้จะทำงานเชิงลึกของโดเมนย่อยของใด ๆ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

ฉันต้องแก้ไขคำตอบของ Larsนิดหน่อยเนื่องจากเด็กกำพร้า\ลงเอยใน regex เพื่อเปรียบเทียบโฮสต์จริงเท่านั้น (ไม่สนใจโปรโตคอลหรือพอร์ต) และฉันต้องการสนับสนุนlocalhostโดเมนนอกเหนือจากโดเมนการผลิตของฉัน ดังนั้นฉันจึงเปลี่ยน$allowedพารามิเตอร์ให้เป็นอาร์เรย์

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

การใช้งานดังต่อไปนี้:

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

0

ในกรณีของฉันใช้มุม

ในตัวสกัดกั้น HTTP ของฉันฉันตั้งค่า

with Credentials: true.

ในส่วนหัวของคำขอ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.