Access-Control-Allow-Originワイルドカードサブドメイン、ポート、プロトコル


312

すべてのサブドメイン、ポート、プロトコルでCORSを有効にしようとしています。

たとえば、http: //sub.mywebsite.com 8080 / からhttps://www.mywebsite.com/へのXHRリクエストを実行できるようにしたい*

通常、一致するオリジンからのリクエストを有効にします(制限されます)。

//*.mywebsite.com:*/*

回答:


207

DaveRandomの回答に基づいて、私も試してみましたがAccess-Control-Allow-Origin、書き換えルールを使用せずに、同じ結果(現在の特定のプロトコル+ドメイン+ポートに動的に設定される)を生成する少し単純なApacheソリューションを見つけました。

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を有効にする場合は、最初の行の正規表現を次の正規表現に置き換えるだけです。

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

注:仕様への準拠と正しいキャッシュ動作のVary: Originために、CORS非対応のリクエストや許可されていない送信元からのリクエストであっても、常にCORS対応リソースの応答ヘッダーを追加します(理由の例を参照)。


1
ほぼこれがあり(Vary Originではありません)、訪問者が同じフォントを使用して複数のサブドメイン間をジャンプすると、動作が悪くなりました。フォントとaccess-control-originヘッダーもキャッシュされました。これに少し変更を加えました。許可されたドメインの1つからのリクエストの場合は、「Access-Control-Allow-Origin *」を使用します。たぶん、これは私たちが以前に持っていなかった「Vary Origin」で解決されました...今もそれを追加しました。
Erik Melkersson、2015

2
プリンシパルドメイン「mywebsite.com」では機能しません
biology.info

1
@ pgmann、//Apache confはスラッシュ区切りの正規表現を使用しないため、このコンテキストでをエスケープする必要はありません。Regexrは、そのコンテキストではスラッシュが区切り文字として特別な意味を持っているため、文句を言います。
Noyo、2016年

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仮想ホスト構成で?
Glen

252

CORS仕様はオールオアナッシングです。*nullまたは正確なプロトコル+ドメイン+ポートのみをサポートします:http : //www.w3.org/TR/cors/#access-control-allow-origin-response-header

サーバーは正規表現を使用して送信元ヘッダーを検証する必要があります。その後、Access-Control-Allow-Origin応答ヘッダーで送信元の値をエコーできます。


7
@Dexter "null"は、 "file://"スキームからCORSリクエストを行う場合など、 "null"オリジンに応答して使用できます。
monsur

130
CORS仕様がOPの正確なユースケースをサポートしないことは、かなり近視眼的です。
2014年

6
@aroth:実際にはそうではありません。仕様では、実装で必要な任意の一致する構文を使用できるようになっています。このユースケースをサポートしない実装では、近視眼になるだけです。つまり、サーバーに指定するのはACAO値ではなく、後者は単なるプロトコルの詳細です。私の仮定は、エコーバックされるオリジンを必要とする、またはそこから恩恵を受けるセキュリティシナリオがあることですが、単純な実装は単に「OK」と言っても機能しません。
2014年

3
2015年からの更新:この回答は不完全であり、キャッシュの問題につながる可能性があるため、有害であると見なされます。適切な実装(Apacheの場合)と説明については、以下の私の回答を参照してください:stackoverflow.com/a/27990162/357774。また、@ arothは、tneが指摘しているように、実際に、OPの正確なユースケースであるw3.org/TR/cors/#resource-implementationを許可しています。そして、この答えが指摘するように、それを実装するかどうかはサーバー次第です。これは、上記の回答でわかるように、3行で実行できます。
Noyo、2015

19
@Noyo-それでは、元の意味を明確にします。CORS仕様では、CORS を実装するすべてのサーバーに、OPの正確なユースケースに対する自動組み込みサポートを提供することを厳密に要求するわけではないことは、かなり近視眼です。カスタムPHPコードを使用して独自のシムを作成したり、ルールを書き換えたり、断片化、バグ、および災害のレシピは、個々のユーザーに任せます。サーバー開発者はそれ以上の知識が必要です。そうでない場合は、CORS仕様で強制する必要があります。
2015

59

編集:これの代わりに@Noyoのソリューションを使用してください。負荷がかかった場合、よりシンプルで明確になり、パフォーマンスが向上する可能性があります。

元の回答は、歴史的な目的のためにここに残されました!!


私はこの問題をいくつか試し、Apacheで動作するこの再利用可能な.htaccess(またはhttpd.conf)ソリューションを思いつきました。

<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.comandに制限します*.sub.mydomain.com(つまり、ドメインルートである必要はありません)。変更が許可されている要素(プロトコル、ポート)は、正規表現のURIマッチング部分を変更することで制御できます。


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"

サイトを有効にするには、上記のApache設定で「mywebsite.com」の代わりにサイトを配置するだけです。

複数のサイトを許可するには:

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()それが正しい方法であるために使用するために編集さ.れました(DNS名で有効な唯一の正規表現メタ文字preg_quote()は意図された操作をより適切に説明しています)
DaveRandom

1
none仕様に従って、がヘッダーの意味的に有効な値ではない(または少なくとも、それが意味することを行わない)ことを明確にする必要があります。そのreturn null;ため、そのブランチにとっては意味があり、その場合はヘッダーをクライアントに送信してはならないので、呼び出し元がチェックする必要があります。
DaveRandom 2015

preg_quote()*記号を引用するため、str_replace()たとえば、孤立した「\」が残ります。
Christoffer Bubach 2016年

1
これは便利です。私のサイトがajaxに「www」があり、パーマリンク構造にはないことに気づくまで、CORSの問題に時間を費やしました-あなたのソリューションは問題がどこにあるかを理解し、それを解決してくれました。
ソル

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

「Cookieドメイン」(www.domain.tld)からフォントを読み取るときに、静的な「Cookieのない」ドメインでFont Awesomeに同様の問題があり、この投稿は私たちのヒーローでした。こちらをご覧ください:「クロスオリジンリソースシェアリング(CORS)応答ヘッダーがありません」というWebフォントの問題を修正するにはどうすればよいですか?

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で機能させるために変更しなければならないものです。これは、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の回答を少し変更する必要\があり、運用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.