So erkennen Sie Cross-Origin-Fehler (CORS) im Vergleich zu anderen Fehlertypen für XMLHttpRequest() in Javascript

Lesezeit: 11 Minuten

Ich versuche zu erkennen, wann ein XMLHttpRequest() aufgrund eines Cross-Origin-Fehlers im Gegensatz zu einer fehlerhaften Anfrage fehlschlägt. Zum Beispiel:

    ajaxObj=new XMLHttpRequest()
    ajaxObj.open("GET", url, true); 
    ajaxObj.send(null);

Betrachten Sie 4 Fälle für die URL:

Fall 1: url ist eine gültige Adresse, bei der access-control-allow-origin richtig eingestellt ist

  • Beispiel: http://192.168.8.35 wo ich einen server mit habe Access-Control-Allow-Origin: * in der Kopfzeile einstellen
  • Dies ist leicht als ajaxObj.readyState==4 und ajaxObj.status==200 zu erkennen

Fall 2: url ist eine ungültige Adresse auf einem vorhandenen Server

  • Beispiel: http://xyz.google.com wo der Server antwortet, aber es ist keine gültige Anfrage
  • Daraus ergibt sich ajaxObj.readyState==4 und ajaxObj.status==0

Fall 3: URL ist zu einer nicht existierenden Server-IP-Adresse

  • Beispiel: http://192.168.8.6 in meinem lokalen Netzwerk, wo es nichts zu antworten gibt
  • Daraus ergibt sich ajaxObj.readyState==4 und ajaxObj.status==0

Fall 4: url ist eine gültige Adresse, wo access-control-allow-origin ist NICHT einstellen

  • Beispiel: http://192.168.8.247 wo ich einen Server habe ohne Access-Control-Allow-Origin: * in der Kopfzeile einstellen
  • Daraus ergibt sich ajaxObj.readyState==4 und ajaxObj.status==0

Das Problem ist: Wie unterscheide ich Fall 4 (Access-Control-Allow-Origin-Fehler) und Fälle 2 & 3?

In Fall 4 zeigt die Chrome-Debug-Konsole den Fehler:

XMLHttpRequest cannot load http://192.168.8.247/. Origin http://localhost is not allowed by Access-Control-Allow-Origin.

Wie mache ich diesen Fehler in Javascript bekannt?

Ich habe versucht, einen Hinweis darin zu finden ajaxObj aber es scheint nichts anders zu sein als in Fall 2 & 3.

Hier ist ein einfacher Test, den ich verwendet habe:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CORS Test</title>
<script type="text/javascript">
function PgBoot()
{
//  doCORS("http://192.168.8.35");   // Case 1
//  doCORS("http://xyz.google.com"); // Case 2
    doCORS("http://192.168.8.6");    // Case 3
//  doCORS("http://192.168.8.247");  // Case 4
}

function doCORS(url)
{
    document.getElementById("statusDiv").innerHTML+="Processing url="+url+"<br>";
    var ajaxObj=new XMLHttpRequest();
    ajaxObj.overrideMimeType('text/xml');
    ajaxObj.onreadystatechange = function()
    {
        var stat=document.getElementById("statusDiv");
        stat.innerHTML+="readyState="+ajaxObj.readyState;
        if(ajaxObj.readyState==4)
            stat.innerHTML+=", status="+ajaxObj.status;
        stat.innerHTML+="<br>";
    }
    ajaxObj.open("GET", url, true); 
    ajaxObj.send(null);
}
</script>
</head>
<body onload="PgBoot()">
<div id="statusDiv"></div>
</body>
</html>

Ergebnisse mit Chrome:

Processing url=http://192.168.8.35
readyState=1
readyState=2
readyState=3
readyState=4, status=200

Processing url=http://xyz.google.com
readyState=1
readyState=4, status=0

Processing url=http://192.168.8.6
readyState=1
readyState=4, status=0

Processing url=http://192.168.8.247
readyState=1
readyState=4, status=0

  • Dies wurde tatsächlich auf stackoverflow.com/q/4844643/632951 gefragt (aber ohne Antwort).

    – Schrittmacher

    24. Januar 2016 um 1:09 Uhr

Benutzer-Avatar
Apsiller

Nein, es gibt keine Möglichkeit, den Unterschied zu erkennen, gemäß der W3C-Spezifikation.

So spezifiziert die CORS-Spezifikation die einfache Cross-Origin-Anfrage Verfahren:

Wenden Sie die Schritte zum Erstellen einer Anfrage an, und beachten Sie beim Erstellen der Anfrage die nachstehenden Anfrageregeln.

Wenn das manuelle Weiterleitungs-Flag nicht gesetzt ist und die Antwort einen HTTP-Statuscode von 301, 302, 303, 307 oder 308 hat: Wenden Sie die Umleitungsschritte an.

Wenn der Endbenutzer die Anfrage abbricht: Wenden Sie die Abbruchschritte an.

Bei einem Netzwerkfehler: Im Falle von DNS-Fehlern, TLS-Aushandlungsfehlern oder anderen Arten von Netzwerkfehlern, Wenden Sie die Schritte für Netzwerkfehler an. Fordern Sie keine Art von Endbenutzerinteraktion an …

Andernfalls: Führen Sie eine Ressourcenfreigabeprüfung durch. Wenn es fehlschlägt, wenden Sie die Netzwerkfehlerschritte an

Im Falle einer fehlgeschlagenen Netzwerkverbindung oder eines fehlgeschlagenen CORS-Austauschs wird die Schritte zu Netzwerkfehlern angewendet werden, so dass es buchstäblich keine Möglichkeit gibt, zwischen den beiden Fällen zu unterscheiden.

Wieso den? Ein Vorteil besteht darin, dass ein Angreifer daran gehindert wird, die Netzwerktopologie eines LANs zu inspizieren. Beispielsweise könnte ein bösartiges Webseitenskript die IP-Adresse Ihres Routers finden, indem es seine HTTP-Schnittstelle anfordert, und dadurch einige Dinge über Ihre Netzwerktopologie erfahren (z. B. wie groß Ihr privater IP-Block ist, /8 oder /16). Da Ihr Router keine CORS-Header sendet (oder nicht senden sollte), lernt das Skript absolut nichts.

  • Danke für die Antwort. Scheint ein Fehler in der CORS-Spezifikation zu sein. mir. Nicht das was ich hören wollte aber trotzdem danke 🙂

    – Benutzer2871305

    11. Oktober 2013 um 22:13 Uhr

  • Das Chrome-Debug-Protokoll zeigt einen sehr spezifischen Fehler, wenn eine CORS-Anfrage fehlschlägt. Können wir das verwenden / verwenden, was es verwendet?

    – Gold

    18. April 2014 um 4:15 Uhr

  • @mgold Nein. Der Browser weiß genau, was schief gelaufen ist, und teilt dem Benutzer mit, wer physisch vor dem Computerbildschirm sitzt. Der Browser teilt dem Skript nichts mit, das möglicherweise von einer fremden Website stammt und von einer unbekannten Person geschrieben wurde, um einen unbekannten (potenziell bösartigen) Zweck zu erreichen.

    – Apsiller

    10. Februar 2016 um 15:05 Uhr


  • Gibt es also keine Möglichkeit zu unterscheiden, welche Art von Netzwerkfehler aufgetreten ist? In einer Web-App können verschiedene Arten von Fehlern unterschiedliche Aktionen seitens des Benutzers zur Wiederherstellung erfordern (z. B. bald erneut versuchen, anstatt das Problem an jemanden zu eskalieren, der sich nicht darum kümmern sollte, wenn ersteres angemessen ist).

    – Michael

    4. Dezember 2017 um 18:37 Uhr

  • @Michael Richtig; Netzwerkfehler und CORS-Fehler (sowie alle Subtypen von Netzwerk- und CORS-Fehlern) sind absichtlich nicht von Skripten zu unterscheiden, die die XHR-API lesen. (Der Benutzer kann auf die Konsole schauen.) Sie könnten versuchen, das Problem experimentell zu bestimmen. Können Sie auf eine öffentliche CORS-fähige Ressource mit hoher Verfügbarkeit zugreifen? Es ist kein Netzwerkausfall. Können Sie auf eine öffentliche CORS-Ressource in Ihrer eigenen Domain zugreifen (die Sie vielleicht genau für diesen Test eingerichtet haben)? Es handelt sich nicht um einen DNS-Auflösungsfehler. Können Sie ein Bild von Ihrer Domain über HTTP, aber nicht über HTTPS laden? Möglicherweise liegt ein Problem bei der TLS-Aushandlung vor.

    – Apsiller

    4. Dezember 2017 um 18:57 Uhr


Benutzer-Avatar
Krishna K

Vielleicht für den Fall, dass es jemandem hilft … eine andere Möglichkeit, den Unterschied zwischen Cors und Netzwerkfehlern zu beheben … kann mit Chrome oder Firefox funktionieren … (allerdings keine perfekte Lösung)

var external="your url";

if (window.fetch) {
    // must be chrome or firefox which have native fetch
    fetch(external, {'mode':'no-cors'})
        .then(function () {
            // external is reachable; but failed due to cors
            // fetch will pass though if it's a cors error
        })
        .catch(function () {
            // external is _not_ reachable
        });
} else {
    // must be non-updated safari or older IE...
    // I don't know how to find error type in this case
}

  • Ich habe mit Holen getestet https://www.google.com (vorhandenes CORS-Ziel) versus https://www.google.invalid (nicht vorhandenes Ziel) und konnte keinen Unterschied im skriptsichtbaren Verhalten feststellen. Welchen Fall soll das abdecken? (Oder hat sich das Verhalten seit November 2016 geändert? Die Spezifikation ändert sich manchmal, insbesondere in Bezug auf die CORS-Sicherheit.)

    – Apsiller

    1. Mai 2017 um 15:11 Uhr


  • Diese Lösung funktioniert nur mit nativem Abruf; Es scheint nicht mit den Polyfills zu funktionieren.

    – Aaron Blenkusch

    8. Januar 2018 um 21:46 Uhr

  • Dies hat den Trick für mich getan, ich konnte mit diesem Snippet “Access-Control-Allow-Origin”-Fehler abfangen. Vielen Dank!

    – fnicollet

    3. Februar 2020 um 9:10 Uhr

Um eine CORS-Verletzung von anderen fehlgeschlagenen AJAX-Anforderungen zu unterscheiden, können Sie die Antwortheader einer HEAD-Anforderung mit untersuchen serverseitiger Code und geben Sie die Ergebnisse an Ihre Kundenseite zurück. Wenn beispielsweise die AJAX-Anfrage fehlschlägt (Status 0), könnten Sie dieses Skript aufrufen (nennen wir es cors.php) und sicher wissen, ob die Antwortheader enthalten Access-Control-* Kopfzeilen.

Beispiele:

cors.php?url=http://ip.jsontest.com
cors.php?url=http://www.google.com
cors.php?url=http://10.0.0.1

kehrt zurück

HTTP/1.1 200 OK Access-Control-Allow-Origin: *
HTTP/1.1 302 gefunden
Ungültige Anfrage


cors.php – Passen Sie nach Bedarf an

<?php /* cors.php */
$url = $_GET["url"];
if(isset($url)) {
    $headers = getHeaders($url);
    header("Access-Control-Allow-Origin: *");

    if(count($headers) == 0) {
        die("Invalid request"); // cURL returns no headers on bad urls
    } else {
        echo $headers[0];       // echo the HTTP status code
    }

    // Include any CORS headers
    foreach($headers as $header) {
        if(strpos($header, "Access-Control") !== false) {
            echo " " . $header;
        }
    }
}

function getHeaders($url, $needle = false) {
    $headers = array();
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 4);        // Timeout in seconds
    curl_setopt($ch, CURLOPT_TIMEOUT, 4);               // Timeout in seconds
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_VERBOSE, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD');    // HEAD request only
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use(&$headers) {
        array_push($headers, $header);
        return strlen($header);
    });
    curl_exec($ch);
    return $headers;
} /* Drakes, 2015 */

Clientseitiger Testrahmen:

function testCORS(url, $elem) {
    $.ajax({
      url: url,
      timeout: 4000
    })
    .fail(function(jqXHR, textStatus) {
       if(jqXHR.status === 0) {
           // Determine if this was a CORS violation or not
           $.ajax({
              context: url,
              url: "http://myserver.com/cors.php?url=" + escape(this.url),
           })
           .done(function(msg) {
              if(msg.indexOf("HTTP") < 0) {
                $elem.text(url + " - doesn't exist or timed out");
              } else if(msg.indexOf("Access-Control-Allow-Origin") >= 0) {
                $elem.text(url + " - CORS violation because '" + msg + "'");
              } else {
                $elem.text(url + " - no Access-Control-Allow-Origin header set");
              }
           });
       } else {
           // Some other failure (e.g. 404), but not CORS-related
           $elem.text(url + " - failed because '" + responseText + "'");
       }
    })
    .done(function(msg) {
      // Successful ajax request
      $elem.text(this.url + " - OK");
    }); /* Drakes, 2015 */
}

Kabelbaumfahrer:

// Create a div and append the results of the URL calls
$div = $("<div>"); 
$("body").append($div);

var urls = ["http://ip.jsontest.com", "http://google.com", "http://10.0.0.1"];
urls.map( function(url) {
   testCORS(url, $div.append("<h4>").children().last());
});

Die Ergebnisse:

http://ip.jsontest.com – OK

http://google.com – kein Access-Control-Allow-Origin-Header gesetzt

http://10.0.0.1 – existiert nicht oder ist abgelaufen

  • Sicherlich wird eine serverseitige Lösung funktionieren, da sich PHP im Gegensatz zu einem Browser nicht um den Access-Control-Allow-Origin-Header kümmert. Diese Lösung funktioniert jedoch nicht für mich, da sich die Anfrage/Antwort vollständig hinter der Firewall befindet. Der Server kann keine GET-Anforderung ausführen, nur der Client.

    – Benutzer2871305

    20. November 2015 um 16:59 Uhr

  • @Drakes, fordern den Server an aus dem Browser? Wenn Sie es von PHP aus tun, beantwortet es diese Frage nicht.

    – Schrittmacher

    24. Januar 2016 um 1:13 Uhr

  • @ Pacerier Warum nicht? Ich habe klar gesagt, zuerst eine AJAX-Anfrage (Client-Seite) zu versuchen, und wenn es fehlschlägt (Error-Handler), dann das cors.php-Skript aufzurufen (wieder mit AJAX, Client-Seite).

    – Draken

    24. Januar 2016 um 2:05 Uhr

  • @Drakes, Hauptsächlich versuchen die Leute, Fehlererkennung mit durchzuführen Nur Javascriptaber Ihre Lösung erfordert, dass der Server als Proxy fungiert.

    – Schrittmacher

    26. Januar 2016 um 5:12 Uhr

  • @ Pacerier, es ist entweder diese funktionierende Lösung oder die obige Antwort “Nein, es ist unmöglich”.

    – Draken

    26. Januar 2016 um 8:20 Uhr

Überraschenderweise können Sie das für Bilder tun (und vielleicht gibt es eine ähnliche Lösung für andere bestimmte Inhaltstypen). Der Clou ist, dass CORS anscheinend nicht beachtet wird, daher Aussage “URL kann nicht von XHR geladen werden && URL kann von img src erfolgreich geladen werden” impliziert, dass die URL funktioniert, aber CORS-blockiert ist.

Dies hilft definitiv nicht in allen Fällen, aber in einigen Fällen (z. B. wenn ein Dienstprogramm erkennt, ob Sie CORS richtig übergeben) kann es ausreichen. Zum Beispiel wollte ich eine Warnung in der Konsole auslösen, wenn meine App (separates Frontend und Backend) mit korrekt konfigurierter URL des Backends und falsch konfiguriertem CORS auf dem Server installiert ist, also war dies für mich vollkommen ausreichend, da ich ein Image zum Testen bereitstellen kann an.

function testCors(url) {
    var myRequest = new XMLHttpRequest();
    myRequest.open('GET', url, true);
    myRequest.onreadystatechange = () => {
        if (myRequest.readyState !== 4) {
            return;
        }
        if (myRequest.status === 200) {
            console.log(url, 'ok');
        } else {
            var myImage = document.createElement('img');
            myImage.onerror = (...args) => {console.log(url, 'other type of error', args);}
            myImage.onload = () => {console.log(url, 'image exists but cors blocked');}
            myImage.src = url;
            console.log(url, 'Image not found');
        }
    };
    myRequest.send();
}

Folgendes habe ich getan – obwohl Sie Änderungen am Remote-Server vornehmen müssen (eine Seite hinzufügen).

  1. Ich habe eine probe.html-Seite erstellt, die in einem Iframe bereitgestellt werden soll (auf dem Remote-Ursprung, den Sie mit CORS kontaktieren möchten).
  2. Ich registriere einen window.onmessage-Handler auf meiner Seite
  3. Ich lade die Seite probe.html mit einem sicheren Iframe in meine Seite
  4. Innerhalb meiner Seite auf dem Iframe-Onload-Ereignis sende ich mit window.postMessage() eine Nachricht an den Iframe.
  5. Die Seite probe.html prüft die URL (vom selben Ursprung innerhalb des iframe)
  6. Die Seite probe.html sendet mir die Antwortdetails xhr.status und xhr.statusText mit window.postMessage()
  7. Wenn der Status ein Fehler ist, protokolliere ich den Status und StatusText an unseren Protokollierungsdienst

Beachten Sie jedoch, dass es sehr schwierig ist, es sicher zu machen, wenn Sie Parameter auf irgendeine Weise übergeben (jeder kann Ihren Iframe einbetten, andere Parteien können postMessage auf der Seite oder im Iframe verursachen).

Und es ist nicht perfekt (es erkennt nur Fehler, die keine vorübergehenden Einzelfälle sind).

Obwohl es viel Arbeit ist, können Sie mehr Informationen über die Ursache eines Fehlers erhalten. Sehr nützlich, wenn Sie nicht direkt auf den Browser Ihrer Benutzer zugreifen können.

PS: Dies unterscheidet sich vollständig von der PHP-Antwort, die vorschlägt, dass Ihr Server mit dem Remote-Server kommunizieren sollte (a), der nicht viel zur Diagnose von Kommunikationsfehlerursachen beiträgt, und (b) die Verwendung von curl von PHP nur nach Ihrem Server fragt sei ernsthaft pwned!

1092630cookie-checkSo erkennen Sie Cross-Origin-Fehler (CORS) im Vergleich zu anderen Fehlertypen für XMLHttpRequest() in Javascript

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy