Піддомени, порти та протоколи підключення підключення підключення доступу, контроль та дозвіл


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})?)$.

Примітка. Для дотримання специфікацій та правильної поведінки кешування ЗАВЖДИ додайте Vary: Originзаголовок відповіді для ресурсів, що підтримуються CORS, навіть для запитів, що не мають CORS, та запитів із забороненим походженням (див. Приклад, чому ).


1
У нас це було майже (не Vary Origin) і погана поведінка, коли відвідувачі перестрибували між кількома піддоменами, використовуючи той самий шрифт Шрифт і заголовок джерела контролю доступу також були кешовані. Я вніс невеликі зміни в цьому: я використовую "Access-Control-Allow-Origin *", якщо запит надходить з одного з наших дозволених доменів. Можливо, це було вирішено за допомогою "Vary Origin", якого у нас не було раніше ... тепер додав і це.
Ерік Мелкерссон

2
Не працює для основного домену 'mywebsite.com'
biology.info

1
@pgmann, //у цьому контексті не потрібно уникати , оскільки Apache conf не використовує регулярні вирази, обмежені косою рисою. Regexr скаржиться на те, що в цьому контексті косої риси мають особливе значення як роздільники.
Нойо

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

Ваш сервер повинен буде перевірити заголовок джерела за допомогою регулярного вираження, і тоді ви можете повторити значення початкового коду в заголовку відповіді Access-Control-Allow-Origin.


7
@Dexter "null" може бути використаний у відповідь на "null" походження, наприклад, коли робиться запит CORS з файлу: // схеми.
монсур

130
Дуже короткозоріше, що специфікація CORS не підтримує точний варіант використання ОП .
aroth

6
@aroth: Насправді специфікація дозволяє реалізаціям використовувати будь-який відповідний синтаксис, який вони хочуть; це було б досить глибоко зафіксовано, щоб реалізація не підтримувала цю ситуацію використання. Іншими словами, те, що ви вказуєте своєму серверу, не є значенням ACAO, останнє - лише деталь протоколу. Моє припущення полягає в тому, що існує сценарій безпеки, який вимагає або вигоди від походження відкликається назад, але наївна реалізація спрацьовує, просто кажучи "ОК" чи ні.
TNE

3
Оновлення від 2015 року: цю відповідь слід вважати шкідливою, оскільки вона неповна і може призвести до проблем кешування. Перегляньте мою відповідь нижче щодо правильної реалізації (для Apache) та пояснення: stackoverflow.com/a/27990162/357774 . Крім того , @aroth як TNE вказує, специфікації на насправді робить дозволяють точно випадок використання в OP: в w3.org/TR/cors/#resource-implementation . І як вказує ця відповідь, на сервері належить реалізувати її. Це можна зробити в 3 рядки, як видно з відповіді, згаданої вище.
Нойо

19
@Noyo - тоді я уточню своє початкове значення. Помічено, що специфікація CORS не вимагає від усіх серверів, які впроваджують CORS, автоматичної, вбудованої підтримки для точного використання ОП . Залишаючи це кожному окремому користувачеві створити свою власну шим за допомогою спеціального PHP-коду, переписати правила чи що-що-що-небудь - це рецепт фрагментації, помилок та катастроф. Серверні розробники повинні знати це краще; а якщо цього не зробити, специфікація CORS повинна змусити їх до цього.
aroth

59

EDIT : Використовуйте рішення @ 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(тобто це не повинно бути коренем домену). Елементи, яким дозволено змінюватись (протокол, порт), можна керувати, змінюючи частину Uge, що відповідає регексу.


22

Я відповідаю на це запитання, оскільки прийнята відповідь не може робити наступного

  1. групування регулярних виразів - це показник ефективності , який не є необхідним.
  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

Тестування Після розгортання:

Наступна відповідь на завиток повинна мати заголовок "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, дійсний у імені DNS, preg_quote()краще описує передбачувану операцію)
DaveRandom

1
Слід уточнити, що noneце не семантично дійсне значення для заголовка (або, принаймні, не робить те, що він має на увазі) відповідно до специфікації. Таким чином, це return null;може мати більше сенсу для цієї гілки, і в такому випадку жоден заголовок не повинен надсилатися клієнтові, тому це повинно перевірятись абонентом.
DaveRandom

preg_quote()цитуватимуть знак * і так, str_replace()наприклад, залишає сироту "\".
Крістоффер Бубах

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, ніхто не працював , як це пропонується в багатьох відповідях на SO, хоча я поняття не маю , якщо ці ключові слова застаріли або не дійсні для 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 на статичному домені, який не містить файлів cookie, коли читали шрифти з "домену cookie" (www.domain.tld), і ця публікація була нашим героєм. Дивіться тут: Як я можу виправити проблему веб-шрифту "Заголовок відсутності перехресного походження (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>

Супер безпечний, супер елегантний. Любіть це: Вам не потрібно відкривати пропускну здатність ваших серверів для злодіїв ресурсів / типів гарячих посилань.

Реквізити для: @Noyo @DaveRandom @ pratap-koritala

(Я намагався залишити це як коментар до прийнятої відповіді, але поки не можу цього зробити)



0

Схоже, оригінальна відповідь була для попереднього Apache 2.4. Мені це не вийшло. Ось що мені довелося змінити, щоб він працював у 2.4. Це буде працювати для будь-якої глибини субдомену вашого company.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

Мені довелося трохи змінити відповідь Ларса , оскільки сирота \потрапила в регулярний вираз, щоб тільки порівняти фактичного хоста (не звертаючи уваги на протокол чи порт), і я хотів підтримати 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.

у заголовку запиту

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.