it-swarm.com.de

Erkennen von Cross Origin (CORS) -Fehlern gegenüber anderen Arten von Fehlern für XMLHttpRequest () in Javascript

Ich versuche zu erkennen, wann ein XMLHttpRequest () aufgrund eines Ursprungsfehlers im Gegensatz zu einer fehlerhaften Anforderung fehlschlägt. Zum Beispiel:

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

Betrachten Sie 4 Fälle für URL:

Fall 1: URL ist eine gültige Adresse, bei der Zugriffskontrollgenehmigung-Origin ordnungsgemäß festgelegt ist

  • Beispiel: http://192.168.8.35 Ich habe einen Server mit Access-Control-Allow-Origin: * im Header
  • Dies kann leicht als ajaxObj.readyState == 4 und ajaxObj.status == 200 erkannt werden

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

  • Beispiel: http://xyz.google.com: Der Server antwortet, ist jedoch keine gültige Anforderung
  • Dies führt zu ajaxObj.readyState == 4 und ajaxObj.status == 0

Fall 3: URL ist eine nicht vorhandene Server-IP-Adresse

  • Beispiel: http://192.168.8.6 in meinem lokalen Netzwerk, wo nichts zu antworten ist
  • Dies führt zu ajaxObj.readyState == 4 und ajaxObj.status == 0

Fall 4: URL ist eine gültige Adresse, unter der Zugriffskontrolle erlaubt ist NICHT einstellen

  • Beispiel: http://192.168.8.247 wo ich einen Server habe ohne Access-Control-Allow-Origin: * im Header gesetzt
  • Dies führt zu ajaxObj.readyState == 4 und ajaxObj.status == 0

Das Problem ist: Wie unterscheide ich Fall 4 (Fehler bei der Zugriffskontrolle zulassen-Ursprung) und Fall 2 und 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?

Ich habe versucht, einen Hinweis in ajaxObj zu finden, aber nichts scheint sich von Fall 2 und 3 zu unterscheiden.

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

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

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

Wenden Sie die Schritte zum Erstellen einer Anforderung an, und beachten Sie die unten aufgeführten Anforderungsregeln, während Sie die Anforderung ausführen.

Wenn das Flag für die manuelle Weiterleitung nicht festgelegt ist und die Antwort einen HTTP-Statuscode von 301, 302, 303, 307 oder 308 hat): Führen Sie die Weiterleitungsschritte aus.

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

Bei einem Netzwerkfehler: Bei DNS-Fehlern, TLS-Aushandlungsfehlern oder anderen Arten von Netzwerkfehlern die Netzwerkfehlerschritte anwenden. Fordern Sie keine Endbenutzerinteraktion an ...

Andernfalls Überprüfen Sie die Ressourcenfreigabe. Wenn die Fehlermeldung fehlschlägt, führen Sie die Netzwerkfehlerschritte ... aus.

Im Falle einer fehlgeschlagenen Netzwerkverbindung oder eines fehlgeschlagenen CORS-Austauschs werden die Netzwerk-Fehlerschritte angewendet, sodass zwischen den beiden Fällen buchstäblich keine Unterscheidung möglich ist.

Warum? Ein Vorteil ist, dass ein Angreifer die Netzwerktopologie eines LANs nicht überprüfen kann. Ein böswilliges Webseitenskript könnte beispielsweise die IP-Adresse Ihres Routers finden, indem er seine HTTP-Schnittstelle anfordert, und daher einige Informationen zur Netztopologie erhalten (z. B. wie groß Ihr privater IP-Block ist, /8 oder /16). Da Ihr Router keine CORS-Header sendet (oder sollte), lernt das Skript absolut nichts.

35
apsillers

Vielleicht hilft es jedem ... eine andere Möglichkeit, den Unterschied zwischen Fehler und Netzwerkfehler zu behandeln ... kann mit Chrome oder Firefox zusammenarbeiten ... (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
}
6
Krishna K

Um eine CORS-Verletzung von anderen fehlgeschlagenen AJAX - Anforderungen zu unterscheiden, können Sie die Antwortheader einer HEAD -Anforderung mit serverseitigem Code überprüfen und die Ergebnisse an Ihre Clientseite zurückgeben. Wenn die Anforderung AJAX beispielsweise fehlschlägt (Status 0), können Sie dieses Skript aufrufen (nennen wir es cors.php) und sicher sein, ob die Antwortheader Access-Control-*-Header enthalten.

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 - Anpassen nach Bedarf

<?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 */

Kundenseitiger Test-Kabelsatz:

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 Headerset für Access-Control-Allow-Origin

http://10.0.0.1 - nicht vorhanden oder abgelaufen

3
Drakes

Überraschenderweise können Sie das für Bilder tun (und vielleicht gibt es eine ähnliche Lösung für andere bestimmte Inhaltstypen). Der Trick ist, dass CORS anscheinend nicht berücksichtigt wird, daher wird mit Anweisung "URL nicht geladen durch XHR && geladen. URL erfolgreich geladen mit img src" impliziert, dass die URL funktioniert, aber CORS blockiert ist.

Dies ist auf jeden Fall nicht in allen Fällen hilfreich, aber in einigen Fällen (z. B. Erkennung des Hilfswerkzeugs, wenn Sie CORS ordnungsgemäß übergeben haben) kann dies den Trick bewirken. Zum Beispiel wollte ich in der Konsole eine Warnung auslösen, wenn meine App (separates Frontend und Backend) mit korrekt konfigurierter URL des Backends und falsch konfigurierten CORS auf dem Server installiert ist. Dies war also völlig ausreichend für mich, da ich ein Image zum Testen bereitstellen kann auf.

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();
}
1
amik

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

  1. Ich habe eine Probe.html-Seite erstellt, die in einem Iframe geliefert werden soll (auf dem entfernten Origin, zu dem Sie CORS verwenden möchten, um Kontakt aufzunehmen)
  2. Ich registriere einen window.onmessage-Handler auf meiner Seite
  3. Ich lade die probe.html-Seite mit einem sicheren Iframe in meine Seite
  4. Auf meiner Seite zum Onload-Ereignis von iframe sende ich eine Nachricht mit window.postMessage () an den iframe.
  5. Die probe.html-Seite prüft die URL (aus dem gleichen Ursprung im iframe)
  6. Die Probe.html-Seite sendet mir die Antwortdetails xhr.status und xhr.statusText mithilfe von window.postMessage ().
  7. Wenn der Status ein Fehler ist, protokolliere ich den Status und den StatusText in unserem Protokollierungsdienst

Beachten Sie jedoch, dass es sehr schwierig ist, die Sicherheit zu gewährleisten, wenn Parameter übergeben werden (jeder kann Ihren iframe einbetten, andere Parteien können postMessage auf der Seite oder dem iframe verursachen).

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

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

PS: Dies unterscheidet sich völlig von der Antwort PHP, die vorschlägt, dass Ihr Server mit dem Remote-Server (a) sprechen sollte, der nicht viel zur Diagnose von Kommunikationsfehlern verwendet wird, und (b) die Verwendung von curl von PHP fragt nur nach einem Server, der ernsthaft pwned ist!

0
robocat