web-dev-qa-db-fra.com

Sous-domaines, ports et protocoles génériques avec caractère générique

J'essaie d'activer CORS pour tous les sous-domaines, les ports et le protocole.

Par exemple, je souhaite pouvoir exécuter une requête XHR depuis http://sub.mywebsite.com:8080/ to https://www.mywebsite.com/ *

En règle générale, j'aimerais activer la requête des origines correspondant (et limitée à): 

//*.mywebsite.com:*/*

243
Elie

Sur la base de answer de DaveRandom, j’ai également joué et trouvé une solution Apache légèrement plus simple qui produit le même résultat (Access-Control-Allow-Origin est défini sur le protocole spécifique actuel + domaine + port de manière dynamique) sans utiliser de règles de réécriture:

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"

Et c'est tout.

Ceux qui souhaitent activer CORS sur le domaine parent (par exemple, mywebsite.com) en plus de tous ses sous-domaines peuvent simplement remplacer l'expression régulière de la première ligne par celle-ci:

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

Remarque: Pour la conformité aux spécifications et le comportement correct de la mise en cache, ajoutez TOUJOURS l'en-tête de réponse Vary: Origin aux ressources activées par CORS, même pour les demandes non CORS et celles provenant d'une origine non autorisée (voir exemple pourquoi ).

155
Noyo

La spécification CORS est tout ou rien. Il ne supporte que *, null ou le protocole exact + domaine + port: http://www.w3.org/TR/cors/#access-control-allow-Origin-response-header

Votre serveur devra valider l'en-tête Origin à l'aide de l'expression rationnelle. Vous pourrez ensuite faire écho à la valeur Origin dans l'en-tête de réponse Access-Control-Allow-Origin. 

212
monsur

EDIT: Utilisez la solution de @ Noyo à la place de celle-ci. C'est plus simple, plus clair et probablement beaucoup plus performant sous charge.

_ {Réponse originale laissée ici pour des raisons historiques uniquement) !!


J'ai joué un peu avec ce problème et proposé cette solution réutilisable .htaccess (ou httpd.conf) qui fonctionne avec 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>

Définissez simplement la variable ACCESS_CONTROL_ROOT en haut du bloc sur votre domaine racine. La valeur de l'en-tête de demande Origin: sera renvoyée au client dans la valeur de l'en-tête de réponse Access-Control-Allow-Origin: si elle correspond à votre domaine.

Notez également que vous pouvez utiliser sub.mydomain.com comme ACCESS_CONTROL_ROOT et limitera les origines à sub.mydomain.com et *.sub.mydomain.com (c’est-à-dire que ce ne sera pas nécessairement la racine du domaine). Les éléments autorisés à varier (protocole, port) peuvent être contrôlés en modifiant la partie correspondant à l'URI de l'expression régulière.

55
DaveRandom

Je réponds à cette question car le réponse acceptée ne peut pas correspondre au domaine principal et ne fonctionne que pour le sous-domaine. De plus, le regroupement regex est un succès, ce qui n'est pas nécessaire.

Par exemple, les en-têtes CORS ne seront pas envoyés pour http://mywebsite.com tant que vous travaillerez pour 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"

Pour activer votre site, il vous suffit de placer votre site à la place de "mywebsite.com" dans la configuration Apache ci-dessus.

Pour autoriser plusieurs sites:

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

Testing Après le déploiement:

La réponse curl suivante doit avoir l'en-tête "Access-Control-Allow-Origin" après la modification.

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

J'avais besoin d'une solution uniquement PHP, donc juste au cas où quelqu'un en aurait aussi besoin. Il faut une chaîne d'entrée autorisée telle que "* .example.com" et renvoie le nom du serveur d'en-tête de requête, si l'entrée correspond.

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

Et voici les cas de test pour un fournisseur de données 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'),
7
Lars

Nous avions des problèmes similaires avec Font Awesome sur un domaine statique "cookie-less" lors de la lecture de polices du "cookie domain" (www.domain.tld) ​​et cet article était notre héros. Voir ici: Comment puis-je résoudre le problème de conflit sur le Web relatif au manquant "En-tête de réponse CORS)"?

Pour les types copier/coller-r (et pour donner quelques accessoires), je l'ai reconstitué à partir de toutes les contributions et je l'ai ajouté en haut du fichier .htaccess de la racine du site:

<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 sécurisé, super élégant. J'adore: vous n'avez pas besoin d'ouvrir la bande passante de vos serveurs aux voleurs de ressources/types de liens dynamiques.

Les accessoires à: @Noyo @DaveRandom @ pratap-koritala 

(J'ai essayé de laisser cela comme un commentaire sur la réponse acceptée, mais je ne peux pas le faire pour l'instant)

1
kanidrive

Lorsque vous définissez Access-Control-Allow-Origin dans .htaccess, seuls les éléments suivants ont fonctionné:

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

J'ai essayé plusieurs autres mots-clés suggérés Header append, Header set, aucun n'a fonctionné comme suggéré dans de nombreuses réponses sur SO, bien que je ne sache pas si ces mots-clés sont périmés ou non valides pour nginx .

Voici ma solution complète:

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]
1
AamirR

Il semble que la réponse originale était pour les versions antérieures à Apache 2.4. Cela n'a pas fonctionné pour moi. Voici ce que j'ai dû changer pour que cela fonctionne dans la version 2.4. Cela fonctionnera pour n’importe quelle profondeur de sous-domaine de 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
Jack K

Pour Spring Boot, j’ai trouvé cette RegexCorsConfiguration qui étend le CorsConfiguration officiel: https://github.com/looorent/spring-security-jwt/blob/master/src/main/Java/be/looorent/security /jwt/RegexCorsConfiguration.Java

0
RiZKiT

Je devais modifier Lars ' répondre un peu, puisqu'un \ orphelin se retrouvait dans la regex, pour ne comparer que l'hôte réel (ne prêtant pas attention au protocole ou au port) et je voulais prendre en charge le domaine localhost en plus de ma production domaine. Ainsi, j'ai changé le paramètre $allowed pour qu'il soit un tableau.

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

Utilisation comme suit:

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