Platzhalter-Subdomains, -Ports und -Protokolle für Zugriffskontrolle-Zulassen-Ursprung

312

Ich versuche, CORS für alle Subdomains, Ports und Protokolle zu aktivieren.

Ich möchte beispielsweise eine XHR-Anforderung von http://sub.mywebsite.com:8080/ an https://www.mywebsite.com/ * ausführen können.

Normalerweise möchte ich Anfragen von Ursprüngen aktivieren, die übereinstimmen (und auf diese beschränkt sind):

//*.mywebsite.com:*/*

Elie
quelle

Antworten:

207

Basierend auf der Antwort von DaveRandom habe ich auch herumgespielt und eine etwas einfachere Apache-Lösung gefunden, die das gleiche Ergebnis liefert ( Access-Control-Allow-Originwird dynamisch auf das aktuelle spezifische Protokoll + Domäne + Port eingestellt), ohne Umschreiberegeln zu verwenden:

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"

Und das ist es.

Diejenigen, die CORS in der übergeordneten Domain (z. B. mywebsite.com) zusätzlich zu all ihren Subdomains aktivieren möchten, können einfach den regulären Ausdruck in der ersten Zeile durch diesen ersetzen:

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

Hinweis: Um die Einhaltung der Spezifikationen und das korrekte Caching-Verhalten zu gewährleisten, fügen Sie IMMER den Vary: OriginAntwortheader für CORS-fähige Ressourcen hinzu, auch für Nicht-CORS-Anforderungen und Anforderungen mit nicht zugelassenem Ursprung (siehe Beispiel warum ).

Noyo
quelle
1
Wir hatten fast dies (nicht Vary Origin) und hatten ein schlechtes Verhalten, als Besucher mit derselben Schriftart zwischen mehreren Subdomains wechselten. Die Schriftart und der Header für den Zugriffssteuerungsursprung wurden ebenfalls zwischengespeichert. Ich habe diesbezüglich eine kleine Änderung vorgenommen: Ich verwende "Access-Control-Allow-Origin *", wenn die Anfrage von einer unserer zulässigen Domains stammt. Vielleicht wurde dies mit "Vary Origin" gelöst, das wir vorher nicht hatten ... jetzt wurde das auch hinzugefügt.
Erik Melkersson
2
Funktioniert nicht für die Hauptdomain 'mywebsite.com'
Biology.info
1
@pgmann, es besteht //in diesem Zusammenhang keine Notwendigkeit, dem zu entkommen , da die Apache-Konf. keine durch Schrägstriche getrennten regulären Ausdrücke verwendet. Regexr beschwert sich, weil in diesem Zusammenhang die Schrägstriche als Begrenzer eine besondere Bedeutung haben.
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
Wo platzieren Sie diesen Code? .htaccess oder in der Apache Virtual Host Konfiguration?
Glen
252

Die CORS-Spezifikation ist alles oder nichts. Es unterstützt nur *, nulloder die genaue Protokoll + domain + port: http://www.w3.org/TR/cors/#access-control-allow-origin-response-header

Ihr Server muss den Ursprungsheader mithilfe des regulären Ausdrucks validieren. Anschließend können Sie den Ursprungswert im Antwortheader Access-Control-Allow-Origin wiedergeben.

Monsur
quelle
7
@Dexter "null" kann als Antwort auf einen "null" -Ursprung verwendet werden, z. B. wenn eine CORS-Anfrage aus einem file: // Schema gestellt wird.
Monsur
130
Es ist zutiefst kurzsichtig, dass die CORS-Spezifikation den genauen Anwendungsfall des OP nicht unterstützen würde .
aroth
6
@aroth: Nicht wirklich, die Spezifikation erlaubt es Implementierungen, jede gewünschte passende Syntax zu verwenden . Es wäre nur zutiefst kurzsichtig, wenn eine Implementierung diesen Anwendungsfall nicht unterstützen würde. Mit anderen Worten, was Sie Ihrem Server angeben, ist nicht der ACAO-Wert, letzterer ist nur ein Protokolldetail. Ich gehe davon aus, dass es ein Sicherheitsszenario gibt, das die Rückmeldung des Ursprungs erfordert oder davon profitiert, aber eine naive Implementierung funktioniert, indem nur "OK" gesagt wird oder nicht.
Der
3
Update von 2015: Diese Antwort sollte als schädlich angesehen werden, da sie unvollständig ist und zu Caching-Problemen führen kann. In meiner Antwort unten finden Sie eine ordnungsgemäße Implementierung (für Apache) und eine Erklärung: stackoverflow.com/a/27990162/357774 . Auch @aroth, wie tne weist darauf hin, die Spezifikation tatsächlich tut dem genauen Anwendungsfall des OP erlauben: w3.org/TR/cors/#resource-implementation . Und wie diese Antwort zeigt, ist es Sache des Servers, sie zu implementieren. Dies kann in 3 Zeilen erfolgen, wie aus der oben genannten Antwort hervorgeht.
Noyo
19
@Noyo - Ich werde dann meine ursprüngliche Bedeutung klären. Es ist zutiefst kurzsichtig, dass die CORS-Spezifikation nicht unbedingt alle Server, die CORS implementieren, zur automatischen, integrierten Unterstützung des genauen Anwendungsfalls des OP verpflichtet . Es ist jedem einzelnen Benutzer überlassen, sein eigenes Shim mit benutzerdefiniertem PHP-Code zu erstellen, Regeln neu zu schreiben oder was Sie haben, ein Rezept für Fragmentierung, Fehler und Katastrophen. Serverentwickler sollten es besser wissen. und wenn nicht, sollte die CORS-Spezifikation sie dazu zwingen.
aroth
59

BEARBEITEN : Verwenden Sie anstelle dieser Lösung die Lösung von @ Noyo . Es ist einfacher, klarer und unter Last wahrscheinlich viel leistungsfähiger.

ORIGINAL ANTWORT HIER NUR ZU HISTORISCHEN ZWECKEN LINKS !!


Ich habe ein bisschen mit diesem Problem herumgespielt und mir diese wiederverwendbare .htaccess-Lösung (oder httpd.conf) ausgedacht, die mit Apache funktioniert:

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

Setzen Sie einfach die ACCESS_CONTROL_ROOTVariable oben im Block auf Ihre Stammdomäne und sie gibt den Origin:Anforderungsheaderwert im Antwortheaderwert an den Client zurück, Access-Control-Allow-Origin:wenn er mit Ihrer Domäne übereinstimmt.

Beachten Sie, dass auch Sie verwenden können , sub.mydomain.comwie das ACCESS_CONTROL_ROOTund es wird begrenzen Herkunft sub.mydomain.comund *.sub.mydomain.com(dh es ist nicht die Domain - Root sein muss). Die Elemente, die variieren dürfen (Protokoll, Port), können durch Ändern des URI-Übereinstimmungsabschnitts der Regex gesteuert werden.

DaveRandom
quelle
22

Ich beantworte diese Frage, weil die akzeptierte Antwort nicht folgen kann

  1. Regex-Gruppierung ist ein Leistungseinbruch , der nicht erforderlich ist.
  2. kann nicht mit der primären Domain übereinstimmen und funktioniert nur für die Subdomain.

Beispiel: Es werden keine CORS-Header für http://mywebsite.com gesendet, während für http://somedomain.mywebsite.com/ gearbeitet wird.

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"

Um Ihre Site zu aktivieren, setzen Sie Ihre Site in der obigen Apache-Konfiguration einfach anstelle von "mywebsite.com" ein.

So erlauben Sie mehrere Sites:

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

Testen Nach der Bereitstellung:

Die folgende Curl-Antwort sollte nach der Änderung den Header "Access-Control-Allow-Origin" haben.

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

Ich brauchte eine reine PHP-Lösung, also nur für den Fall, dass jemand sie auch braucht. Es wird eine zulässige Eingabezeichenfolge wie "* .example.com" verwendet und der Name des Anforderungsheader-Servers zurückgegeben, wenn die Eingabe übereinstimmt.

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

Und hier sind die Testfälle für einen phpunit-Datenanbieter:

//    <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
quelle
1
+1, bearbeitet, um verwendet zu werden, preg_quote()da dies der richtige Weg ist (obwohl .das einzige reguläre Regexp-Metazeichen in einem DNS-Namen gültig ist, preg_quote()beschreibt es die beabsichtigte Operation besser)
DaveRandom
1
Es sollte klargestellt werden, dass nonees sich nicht um einen semantisch gültigen Wert für den Header handelt (oder zumindest nicht das tut, was er impliziert), gemäß der Spezifikation. Daher ist dies return null;für diesen Zweig möglicherweise sinnvoller. In diesem Fall sollte kein Header an den Client gesendet werden, sodass er vom Anrufer überprüft werden sollte.
Dave Random
preg_quote()wird das * -Zeichen zitieren und so str_replace()hinterlässt das zum Beispiel ein verwaistes "\".
Christoffer Bubach
1
Dies ist nützlich. Ich habe einige Zeit mit dem CORS-Problem verbracht, bis mir klar wurde, dass meine Website im Ajax "www" enthält, aber nicht in der Permalink-Struktur. Ihre Lösung hat mir geholfen, zu verstehen, wo das Problem liegt, und das für mich gelöst.
Sol
3

Bei der Einstellung Access-Control-Allow-Originin .htaccess hat nur Folgendes funktioniert:

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

Ich habe versucht , einige andere vorgeschlagene Keywords Header append, Header set, keine gearbeitet wie in vielen Antworten auf SO vorgeschlagen, obwohl ich keine Ahnung, wenn diese Keywords veraltet sind oder nicht gültig für nginx .

Hier ist meine Komplettlösung:

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

Wir hatten ähnliche Probleme mit Font Awesome in einer statischen "Cookie-freien" Domain, als wir Schriften aus der "Cookie-Domain" (www.domain.tld) ​​lasen, und dieser Beitrag war unser Held. Siehe hier: Wie kann ich das Webfont-Problem "Fehlender CORS-Antwortheader (Cross-Origin Resource Sharing)" beheben?

Für die Copy / Paste-R-Typen (und um einige Requisiten zu geben) habe ich dies aus allen Beiträgen zusammengesetzt und oben in der .htaccess-Datei des Site-Stamms hinzugefügt:

<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 sicher, super elegant. Ich liebe es: Sie müssen die Bandbreite Ihres Servers nicht für Ressourcendiebe / Hotlinker-Typen öffnen.

Requisiten an: @Noyo @DaveRandom @ pratap-koritala

(Ich habe versucht, dies als Kommentar zur akzeptierten Antwort zu hinterlassen, aber das kann ich noch nicht.)

Kanidrive
quelle
0

Es sieht so aus, als ob die ursprüngliche Antwort für Pre-Apache 2.4 war. Bei mir hat es nicht funktioniert. Folgendes musste ich ändern, damit es in 2.4 funktioniert. Dies funktioniert für jede Tiefe der Subdomain von 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"
Jack K.
quelle
0

Ich musste Lars ' Antwort ein wenig ändern , da ein verwaister \Mensch im regulären Ausdruck landete, um nur den tatsächlichen Host zu vergleichen (ohne auf das Protokoll oder den Port zu achten), und ich wollte localhostneben meiner Produktionsdomäne auch eine Domäne unterstützen. Daher habe ich den $allowedParameter in ein Array geändert .

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

Verwendung wie folgt:

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

in meinem Fall mit eckigen

in meinem HTTP Interceptor habe ich gesetzt

with Credentials: true.

im Header der Anfrage

Hosam Hemaily
quelle