CORS für XHR in IE10

Die vierte Plattform von IE10 vereinfacht durch Unterstützung von Cross-Origin Resource Sharing (CORS) für XMLHttpRequest (XHR) das Entwickeln von websiteübergreifenden Szenarien, die in verschiedenen Browsern konsistent ausgeführt werden. CORS für XHR vereinfacht das websiteübergreifende Freigeben von Daten und erhöht die Flexibilität. Im einfachsten Szenario ermöglicht CORS das Erstellen von Datenquellen, auf die von beliebigen Websites zugegriffen werden kann. Mit ein paar Kniffen können Sie zugelassene Websites einschränken, Datenänderungen unterstützen und sogar Authentifizierung zulassen. Vor allem gewährleistet CORS die Sicherheit vorhandener Websites durch die Anforderung von Serverbeteiligung.

Einfaches ursprungsübergreifende XHR

Wir wollen nun eine ursprungsübergreifende XHR-Anforderung mit einer nicht ursprungsübergreifenden Anforderung vergleichen. Im Skript ist die URL der einzige Unterschied, die an die offene Methode übergeben wird. Angenommen, wie arbeiten an einem Skript, das eine Liste mit Fotoalben abruft.

Herkömmliche XHR

// Script running on https://photos.contoso.com

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "/albums", true);

xhr.send();

Nun soll auf die Liste von einem anderen Ursprung aus zugegriffen werden. Bei diesem anderen Ursprung kann es sich um eine ganz andere Domäne oder um einen anderen Host mit gleicher Basisdomäne handeln. Unabhängig davon reicht es aus, auf die vollständige URL zu zeigen, damit der Browser automatisch eine CORS-Anforderung sendet.

XHR mit CORS

// Script running on https://www.contoso.com

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "https://photos.contoso.com/albums", true);

xhr.send();

Websites können mithilfe von Funktionserkennung Fallback-Funktionen für ältere Browser bereitstellen. Als Ansatz empfiehlt es sich, nach „withCredentials“ zu suchen, da dies direkt mit der CORS-Unterstützung für XHR zusammenhängt.

CORS-aktivierte XHR mit Funktionserkennung

// Script running on https://www.contoso.com

var xhr = new XMLHttpRequest();

if ("withCredentials" in xhr) {

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "https://photos.contoso.com/albums", true);

xhr.send();

} else {

// Fallback behavior for browsers without CORS for XHR

}

Zu diesem Zeitpunkt sendet der Clientcode eine CORS-Anforderung direkt an „https://photos.contoso.com“. Die Anforderung gibt jedoch keine Daten zurück. Dieser Fehler tritt auf, da der Server noch nicht beteiligt ist. Ein kurzer Blick auf die Entwicklertools verrät uns, was schiefgelaufen ist.

Screenshot der F12-Tools; es wurde kein Header vom Typ „Access-Control-Allow-Origin“ gefunden.

Hier wird deutlich, dass der Server als Antwort einen Header vom Typ „Access-Control-Allow-Origin“ senden muss. In unserem Szenario gewähren wir nicht beliebigen Websites Zugriff auf unsere Alben, sondern lediglich „https://www.contoso.com“. Um dies zu ermöglichen, muss der Server den Ursprung der Anforderung identifizieren. In der ausgehenden Anforderung finden wir einen neuen Header mit genau diesen Informationen: „Origin“.

Einfache Header von CORS-Anforderungen

GET https://photos.contoso.com/albums HTTP/1.1

Origin: https://www.contoso.com

...

Mithilfe dieser Informationen kann der Server den Zugriff auf eine beliebige Reihe von Websites einschränken. Wenn der Server jedes Mal einen Header vom Typ „Access-Control-Allow-Origin“ mit dem Wert „*“ hinzufügt, wird allen Websites Zugriff gewährt. In unserem Szenario soll der Server den Ursprung überprüfen und anschließend „Access-Control-Allow-Origin“ so festlegen, dass nur „https://www.contoso.com“ Zugriff erhält.

Einfache Header von CORS-Antworten

HTTP/1.1 200 OK

Access-Control-Allow-Origin: https://www.contoso.com

...

Mit den o. g. vorgenommenen Aktualisierungen kann der „https://www.contoso.com“-Client nun unter „https://photos.contoso.com“ auf die Albumlisten zugreifen.

Ursprungsübergreifende XHR mit Preflight

Die „einfachen“ CORS-Anforderungen, die wir bisher erläutert haben, eignen sich hervorragend für schreibgeschützte Szenarien, wie z. B. zum Herunterladen eines Fotoalbums. Für den nächsten Schritt, das websiteübergreifende Bearbeiten von Daten, ist jedoch ein höherer Aufwand auf dem Server erforderlich. Nehmen wir an, wir fügen dem Client Code hinzu, um ein neues Album zu erstellen.

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("PUT", "https://photos.contoso.com/albums", true);

xhr.send(JSON.stringify({ name: "New Album" }));

Dies wie besehen auszuführen, funktioniert nicht. Eine Analyse des Netzwerkverkehrs zeigt, dass eine Anforderung gesendet wurde – jedoch nicht die erwartete.

Screenshot der F12-Tools mit Anzeige einer OPTIONS-Preflightanforderung

Tatsächlich hat der Browser eine Preflightanforderung gesendet. Preflightanforderungen werden vor Anforderungen, die möglicherweise zu Datenänderungen auf dem Server führen, gesendet. Diese Anforderungen können anhand vorhandener, nicht einfacher Eigenschaften gemäß der CORS-Spezifikation identifiziert werden. Diese Eigenschaften reichen von bestimmten HTTP-Methoden wie „PUT“ bis hin zu benutzerdefinierten HTTP-Headern. Browser senden Preflightanforderungen, um den Server um Erlaubnis zu bitten, die tatsächliche Anforderung zu senden. In unserem Beispiel wird es dem Browser gestattet, eine „PUT“-Anforderung zu prüfen.

Preflightanforderung

OPTIONS https://photos.contoso.com/albums HTTP/1.1

Origin: https://www.contoso.com

Access-Control-Request-Method: PUT

...

Damit der Browser die tatsächliche Anforderung sendet, sind verschiedene Änderungen auf dem Server erforderlich. Betrachten wir noch einmal die Entwicklertools, um ausführlichere Informationen zu erhalten.

Screenshot der F12-Tools; es wurde keine Liste vom Typ „Access-Control-Allow-Methods“ gefunden.

In einem ersten Schritt muss der Server die „OPTIONS“-Preflightanforderung von den anderen Anforderungen für die gleiche URL unterscheiden. Der Server überprüft die Preflightanforderung, indem er sicherstellt, dass „Access-Control-Request-Method“ über einen zulässigen Ursprung nach „PUT“ fragt. Anschließend sendet er mithilfe des Headers vom Typ „Access-Control-Allow-Methods“ die entsprechende Genehmigung.

Preflightantwort

HTTP/1.1 200 OK

Access-Control-Allow-Origin: https://www.contoso.com

Access-Control-Allow-Methods: PUT

...

Nachdem das Preflightverfahren abgeschlossen und genehmigt wurde, kann die tatsächliche Anforderung ausgeführt werden.

Tatsächliche Anforderung

PUT https://photos.contoso.com/albums HTTP/1.1

Origin: https://www.contoso.com

...

Das Hinzufügen des Albums ist nun aus technischer Sicht abgeschlossen. Der Clientcode kann dies jedoch nicht feststellen, sofern der Server nicht entsprechend reagiert. Vor allem muss der Server weiterhin „Access-Control-Allow-Origin“ in die Antwort einfügen.

Tatsächliche Antwort

HTTP/1.1 200 OK

Access-Control-Allow-Origin: https://www.contoso.com

...

Hiermit kann der Client ein neues Album ursprungsübergreifend hinzufügen und erkennen, ob die Aktion erfolgreich abgeschlossen wurde.

Die nächsten Schritte

Das Kombinieren von CORS mit anderen neuen Plattformfeatures ermöglicht interessante Szenarien. Ein Beispiel ist der websiteübergreifende Uploadtest, in dem ursprungsübergreifende Dateiuploads mithilfe von CORS, XHR, FileAPI und Fortschrittsereignissen verfolgt werden.

– Tony Ross, Program Manager, Internet Explorer