comment contourner Access-Control-Allow-Origin?

197

Je fais un appel ajax à mon propre serveur sur une plate-forme qu'ils définissent empêcher ces appels ajax (mais j'en ai besoin pour récupérer les données de mon serveur pour afficher les données récupérées de la base de données de mon serveur). Mon script ajax fonctionne, il peut envoyer les données au script php de mon serveur pour lui permettre de traiter. Cependant, il ne peut pas récupérer les données traitées car elles sont bloquées par"Access-Control-Allow-Origin"

Je n'ai pas accès à la source / au cœur de cette plateforme. donc je ne peux pas supprimer le script qui m'interdit de le faire. (P / SI a utilisé la console de Google Chrome et a découvert cette erreur)

Le code Ajax comme indiqué ci-dessous:

 $.ajax({
     type: "GET",
     url: "http://example.com/retrieve.php",
     data: "id=" + id + "&url=" + url,
     dataType: 'json',   
     cache: false,
     success: function(data)
      {
        var friend = data[1];              
        var blog = data[2];           
        $('#user').html("<b>Friends: </b>"+friend+"<b><br> Blogs: </b>"+blog);

      } 
  });

ou existe-t-il un JSONcode équivalent au script ajax ci-dessus? Je pense que JSONc'est permis.

J'espère que quelqu'un pourrait m'aider.

ETAN
la source
jusqu'à présent, toutes les réponses à votre question expliquaient un moyen de réécrire le code de votre serveur pour que vous ajax fonctionne. Aucun d'entre eux ne concerne le contournement, comme vous l'avez demandé spécifiquement dans votre question. Avez-vous trouvé un moyen de contourner cet en-tête? Je doute vraiment qu'il y en ait un.
Moradnejad
il n'y a aucun moyen de le contourner. mais vous pouvez mettre un fichier sur votre backend qui exécute la demande. Vous appelez donc par ajax le fichier sur votre propre serveur, ce fichier charge les données de retrieve.php et les renvoie à votre javascript. Dans ce cas, aucune règle CORS ne vous bloque.
Jona Paulus

Réponses:

367

Mettez ceci au-dessus de retrieve.php:

header('Access-Control-Allow-Origin: *');  

Notez que cela désactive efficacement la protection CORS et expose vos utilisateurs aux attaques. Si vous n'êtes pas complètement certain que vous devez autoriser toutes les origines, vous devez le verrouiller sur une origine plus spécifique:

header('Access-Control-Allow-Origin: https://www.example.com')

Veuillez vous référer à la réponse suivante de la pile pour une meilleure compréhension de Access-Control-Allow-Origin

https://stackoverflow.com/a/10636765/413670

Rafay
la source
54
C'est plutôt dangereux. Consultez ma réponse en bas.
Rob
3
tnx, mais vous ne devez pas autoriser l'accès à toutes les origines comme mentionné par @RobQuist dans son commentaire, et dans sa réponse a fourni une meilleure approche
Rafay
2
J'ai donc trouvé cette page car j'avais besoin de "contourner" le contrôle d'accès sur un serveur. La solution ici ne contourne rien, mais configure simplement correctement le contrôle d'accès sur son propre serveur. Si quelqu'un a besoin de contourner cela, il peut utiliser file_get_contents ($ remote_url) ;. Il y a évidemment plusieurs façons de le faire, mais c'est comme ça que je l'ai fait.
Shawn Whinnery
1
@ShawnWhinnery qui est essentiellement l'acte de "mandataire". Bonne solution si vous voulez vraiment charger dynamiquement des données à partir d'un autre site Web dont vous n'avez aucun contrôle.
Rob
1
voulait exécuter le script PHP à partir du noyau dotnet - déplacé le script php vers mon autre URL mais obtenait une erreur de script intersite. a ajouté le code que vous avez montré en haut de PHP et a parfaitement fonctionné. Merci!
raddevus
291

D'accord, mais vous savez tous que le * est un caractère générique et autorise les scripts intersites de tous les domaines?

Vous souhaitez envoyer plusieurs en- Access-Control-Allow-Origintêtes pour chaque site qui est autorisé à - mais malheureusement, il n'est officiellement pas pris en charge pour envoyer plusieurs en- Access-Control-Allow-Origintêtes ou pour mettre plusieurs origines.

Vous pouvez résoudre ce problème en vérifiant l'origine et en renvoyant celui-ci dans l'en-tête, si cela est autorisé:

$origin = $_SERVER['HTTP_ORIGIN'];
$allowed_domains = [
    'http://mysite1.com',
    'https://www.mysite2.com',
    'http://www.mysite2.com',
];

if (in_array($origin, $allowed_domains)) {
    header('Access-Control-Allow-Origin: ' . $origin);
}

C'est beaucoup plus sûr. Vous voudrez peut-être éditer la correspondance et la changer en une fonction manuelle avec une expression régulière, ou quelque chose comme ça. Au moins, cela ne renverra qu'un en-tête, et vous serez sûr que c'est celui d'où provient la demande. Veuillez noter que tous les en-têtes HTTP peuvent être usurpés, mais cet en-tête est destiné à la protection du client. Ne protégez pas vos propres données avec ces valeurs. Si vous voulez en savoir plus, lisez un peu sur CORS et CSRF.

Pourquoi est-ce plus sûr?

Autoriser l'accès à partir d'autres emplacements, puis votre propre site de confiance permet de détourner la session. Je vais aller avec un petit exemple - l'image Facebook permet une origine générique - cela signifie que vous pouvez créer votre propre site Web quelque part, et le faire lancer des appels AJAX (ou ouvrir des iframes) vers Facebook. Cela signifie que vous pouvez récupérer les informations enregistrées du facebook d'un visiteur de votre site Web. Pire encore - vous pouvez écrire des POSTrequêtes et publier des données sur le facebook de quelqu'un - juste pendant qu'il navigue sur votre site Web.

Soyez très prudent lorsque vous utilisez les en- ACAOtêtes!

Rob
la source
12
Je pense que vous devez mettre http: // devant chaque élément de la liste. Au moins, je l'ai fait pour un site sur lequel je travaillais.
blak3r
2
Malheureusement, cela ne semble pas fonctionner. Je crois qu'une seule exception peut être fournie par appel à header ().
lewsid
5
@Shanimal & lewsid -> Je suppose que la virgule séparée ne fonctionne pas vraiment. Référence: w3.org/TR/cors
Rob
3
Pour traiter une liste de domaines, voici une réponse pertinente: stackoverflow.com/a/1850482/766177
Valentin Despa
13
C'est inutile d'ajouter 4 en-têtes comme ça car chaque appel à header()remplace l'en-tête précédent du même type. Donc, tout ce que vous faites est de définir le dernier en-tête. L' entrée manuelle indique que vous pouvez définir un deuxième paramètre de falsepour empêcher l'écrasement de l'en-tête précédent.
BadHorsie
31

Attention , Chrome (et d'autres navigateurs) se plaindra que plusieurs en-têtes ACAO sont définis si vous suivez certaines des autres réponses.

L'erreur sera quelque chose comme XMLHttpRequest cannot load ____. The 'Access-Control-Allow-Origin' header contains multiple values '____, ____, ____', but only one is allowed. Origin '____' is therefore not allowed access.

Essaye ça:

$http_origin = $_SERVER['HTTP_ORIGIN'];

$allowed_domains = array(
  'http://domain1.com',
  'http://domain2.com',
);

if (in_array($http_origin, $allowed_domains))
{  
    header("Access-Control-Allow-Origin: $http_origin");
}

la source
6
C'est une solution encore meilleure que celle que j'ai publiée.
Rob
7

J'ai résolu ce problème lors de l'appel d'un contrôleur MVC3. J'ai ajouté:

Response.AddHeader("Access-Control-Allow-Origin", "*"); 

avant mon

return Json(model, JsonRequestBehavior.AllowGet);

Et aussi, je me $.ajaxplaignais qu'il n'accepte pas l'en - tête Content-type dans mon appel ajax, donc je l'ai commenté car je sais que son JSON est passé à l'action.

J'espère que cela pourra aider.

Atif Rehman
la source
2

C'est une très mauvaise idée à utiliser *, ce qui vous laisse largement ouvert aux scripts intersites. Vous voulez essentiellement votre propre domaine tout le temps, limité à vos paramètres SSL actuels et éventuellement à des domaines supplémentaires. Vous souhaitez également qu'ils soient tous envoyés dans un seul en-tête. Ce qui suit autorisera toujours votre propre domaine dans la même portée SSL que la page actuelle et peut éventuellement inclure également un nombre illimité de domaines supplémentaires. Il les enverra tous sous la forme d'un seul en-tête et remplacera les précédents si quelque chose d'autre les a déjà envoyés pour éviter que le navigateur ne se moque de l'envoi de plusieurs en-têtes de contrôle d'accès.

class CorsAccessControl
{
    private $allowed = array();

    /**
     * Always adds your own domain with the current ssl settings.
     */
    public function __construct()
    {
        // Add your own domain, with respect to the current SSL settings.
        $this->allowed[] = 'http'
            . ( ( array_key_exists( 'HTTPS', $_SERVER )
                && $_SERVER['HTTPS'] 
                && strtolower( $_SERVER['HTTPS'] ) !== 'off' ) 
                    ? 's' 
                    : null )
            . '://' . $_SERVER['HTTP_HOST'];
    }

    /**
     * Optionally add additional domains. Each is only added one time.
     */
    public function add($domain)
    {
        if ( !in_array( $domain, $this->allowed )
        {
            $this->allowed[] = $domain;
        }
    /**
     * Send 'em all as one header so no browsers grumble about it.
     */
    public function send()
    {
        $domains = implode( ', ', $this->allowed );
        header( 'Access-Control-Allow-Origin: ' . $domains, true ); // We want to send them all as one shot, so replace should be true here.
    }
}

Usage:

$cors = new CorsAccessControl();

// If you are only authorizing your own domain:
$cors->send();

// If you are authorizing multiple domains:
foreach ($domains as $domain)
{
    $cors->add($domain);
}
$cors->send();

Vous avez eu l'idée.

mopsyd
la source
1

Avez-vous essayé d'ajouter réellement l'en-tête Access-Control-Allow-Origin à la réponse envoyée par votre serveur? Comme Access-Control-Allow-Origin: *,?

Daniel Brockman
la source
1
Il s'agit d'un en-tête HTTP que votre serveur envoie pour informer le navigateur qu'il est correct de révéler le résultat au script appelant malgré le fait que le domaine d'origine du script ne correspond pas au domaine du serveur. Renseignez-vous sur le partage de ressources d'origine croisée !
Daniel Brockman