Was ist die Same Origin Policy (SOP)

Es handelt sich hierbei um ein Sicherheitskonzept, welches für Clientseitig (im Browser) ausgeführte Zugriffe auf Daten vorschreibt, dass sich diese unter derselben Origin befinden müssen.
 Dabei reden wir von Zugriffen über Javascript und auch Cascading Style Sheets (CSS).

Wenn also eine Seite unter http://www.virtual7.de eine CSS Datei oder JSON Daten über JavaScript auf dem Server https://twitter.com/virtual7 abfragen möchte, würde dies der Browser verbieten.
 Der Grund ist hierbei, dass wir Daten aus einer anderen „Origin“ abrufen wollen.

Der Hintergrund ist ganz einfach, wird JavaScript auf einer Webseite eingebunden, habe ich vollen Zugriff auf das DOM und Cookies somit könnte ich sämtliche Inhalte manipulieren.

Der SOP Schutz des Browsers betrifft hier nur die Response, diese wird nicht zurückgeliefert.
 Sollten wir allerdings ein POST, PUT oder DELETE an einen Server senden, so kann dieser durchaus unsere Daten verarbeiten, jedoch keinen passenden Status dazu zurückliefern.

Weitere Informationen zu SOP finden Sie hier.

Was ist eine Origin?

Eine Origin besteht aus allen Teilen der URL http://www.virtual7.de beinhaltet hier 3 Teile.

  • Das Protokoll: http
  • Den Host: www.virtual7.de
  • Der Port: 80 ist hier nicht sichtbar, da 80 für den Browser Standard ist.

Eine Tabelle mit Beispielen finden sie hier.

Webseiten Architekturen

Wie sind Webseiten Architekturen aufgebaut und warum betrifft uns die Same Origin Policy überhaupt?
 Nun das hängt nun natürlich vor allem von der Architektur die wir verwenden ab, haben wir eine einfache Webseite, welche auf ihrem eigenen Server gehostet direkt im Backend verbunden ist, oder eher eine Webanwendung, welche hauptsächlich über REST mit ihrem Backend kommuniziert und lose gekoppelt ist?

Aber selbst eine einfache Webseite kann schon auf die SOP stoßen, wenn man zum Beispiel JQuery oder ähnliche Bibliotheken von einem CDN lädt überwindet man die Same Origin Policy.

Einfache Architektur mit einem Server
Moderne Architektur für Web-Anwendungen

Wie man sieht, werden für die Webanwendung, Requests vom Frontend-Server direkt zum Backend oder verschiedenen Services gesendet. Da wir also vom Frontend-Server aus seiner Origin auf andere Services zugreifen wollen, durchbrechen wir die Same Origin Policy, die Frage ist nur wie?

Wie umgeht man die Same Origin Policy

Es gibt 3 Wege die im Normalfall genutzt werden um Einschränkungen durch SOP zu umgehen.
 Davon werden in den meisten Fällen aber nur zwei Methoden auch in modernen Anwendungen genutzt.

  1. JSONP
  2. CORS
  3. Proxy

JSONP

Die älteste Methode und zugleich die unsicherste Methode ist JSONP.
 Hier wird durch den Missbrauch eines Script Tag ein JSON Objekt oder auch eine JavaScript Response an eine Callback Funktion weitergegeben.

<script type="application/javascript" src="http://server.example.com/Users/1234?callback=parseResponse"> </script>

In diesem Beispiel würde das JSON in der folgenden Methode ausgelesen werden.

parseResponse({"Name": "Foo", "Id": 1234, "Rank": 7});

JSONP bedeutet, JSON with Padding, das Padding ist in diesem Fall die Callback Methode die in der Rückgabe das JSON Objekt umschließt.

Der angefragte Server muss in diesem Fall JSONP unterstützen und das JSON mit der richtigen Callback Methode zurückgeben, so dass das JSON verarbeitet werden kann.
 Im ersten Moment sieht dies ja auch nach einer akzeptablen Lösung aus.

Doch wenn wir uns das Ganze noch einmal genau anschauen, fällt uns auf wir führen hier Code von einem Server ohne Überprüfung in unserer Origin aus. Wir müssen dem Server vollkommen vertrauen und hoffen, dass die Response dem entspricht was wir erwarten.
 Wenn der Server allerdings uns Schaden möchte, kann er ohne Probleme unsere Daten auslesen oder das DOM manipulieren.

Cross-Origin Resource Sharing (CORS)

Eine weitere Methode ist das setzten der sogenannten CORS Header auf dem Server.

Access-Control-Allow-Origin: http://virtual7.de

Dieser Header kann vom Server an den Browser mitgesendet werden und erlaubt uns nun eine Ressource zu nutzen, die von einer anderen Origin stammt.

Als Parameter für den Header kann man eine oder mehrere Origins  übergeben oder auch eine Wildcard „*“, die Wildcard erlaubt den Zugriff von jeder Origin.

Wir können hier aber nicht nur die Origin beschränken sondern auch noch die einzelnen HTTP-Methoden:

Access-Control-Allow-Methods:
GET

Mit diesem zweiten Header lassen wir nur GET Aufrufe zu und blockieren weiterhin das lesen der Response von POST, PUT oder DELETE Aufrufen.

Der Vorteil dieser Variante liegt klar auf der Hand im Gegensatz zu JSONP habe ich hier keinen Mehraufwand für die Response. Der Server kann einfach seine REST-Schnittstelle freigeben und dem Browser den Zugriff über Header erlauben.
 Alle gängigen Browser unterstützen diese Header und erlauben uns so die Same Origin Policy zu umgehen.

Allerdings bietet auch CORS nicht nur Vorteile.
 Einer der Nachteile ist vor allem, dass jeder Server/Service diese Header setzten muss. Dies führt aber auch dazu, dass der Service wissen muss welche Origin hat der Client.

Wenn nun also jemand einen Client auf einer weiteren Origin angelegt, müssen auch die Services mit geändert werden.

Jeder Service muss von außen ansprechbar sein, da die Webanwendung direkt mit den Services redet. Dies bedeutet aber auch, dass ein Zugriff von überall mit jedem Client möglich ist. Nur ein Browser unterstützt die Same Origin Policy, nutze ich einen direkten REST Client umgehe ich diese Sicherheitsregel des Browsers.

Eine Architektur mit CORS-Header

PROXY

Nutze ich einen Proxy Server, am besten direkt den Server auf dem meine Webanwendung gehostet wird, kann dieser für mich die Anfragen an die Services senden.
 Wie oben erwähnt, gilt die Same Origin Policy nur für den Browser, ein Server unterliegt dieser Bedingung nicht.

Wenn ich also nun auf meinem Frontend Server unter derselben Origin wie auch meine Webanwendung hat, eine Ressource hoste, welche einen Proxy stellt, kann ich die Same Origin Policy einfach umgehen.
 
Es reicht hier wenn der Proxy Server die Anfragen 1:1 weitergibt, ich kann also die Origin und Ressource die ich anfragen möchte einfach dem Proxy senden, dieser leitet die Anfrage im Hintergrund weiter und ich erhalte die Antwort ohne weitere Header oder ähnliches.

Natürlich muss ich die Proxy Implementierung auch erst schreiben, aber sobald diese vorhanden ist müssen die Services nicht mehr wissen in welcher Origin sich ein Client befindet.
 
Wenn man nicht will, das der Proxy für einen Call an einen beliebigen Server im Internet missbraucht wird, kann man seine Services URI’s auch in eine Map hinterlegen.  Dadurch kann ich in der Webanwendung einfach einen Key als Parameter mitliefern und mein Proxy kennt dann das passende URI.

Ein weiterer Vorteil findet sich darin, dass die Services nun nicht mehr direkt vom Client angesprochen werden. Es ist also nun möglich auch den Proxy als einziges Gateway zu den Servern zu nutzen.

Die Angular CLI bietet uns mit ihrer Möglichkeit einer Proxy Config, auch schon in der Entwicklung einfach die Möglichkeit einen Proxy zu verwenden.

Fazit:

Methode HTTP-Verben Server Änderungen Anmerkung
JSONP Nur GET Ja, man muss ein Script umklammert von einer Callback-Funktion zurück geben Man kann nur GET Aufrufe verwenden.
Dem Server muss vertraut werden, man hat keine Kontrolle über den Code der ausgeführt wird im Client.
CORS Alle Verben Ja, die nötigen Header müssen von den Services gesetzt werden. Jeder Origin muss im Service freigegeben werden. Muss bei jedem Service gemacht werden. Jeder Service muss zugänglich sein für den Client. Die nötigen Header können auch von Firewalls gefiltert werden.
Proxy Alle Verben Ja, man muss einen Proxy implementieren. Der Frontend-Server sendet die Anfragen an die Services. Nur der Frontend-Server muss von außen erreichbar sein.

Es ist also möglich SOP, die Same Origin Policy, auf verschiedene Weisen zu überwinden. JSONP würde ich allerdings nicht empfehlen.

Ob man nun CORS oder einen Proxy verwendet ist eine Entscheidung die von Projekt zu Projekt unterschiedlich sein kann. Will ich keinen Proxy schreiben, ist ein CORS Header schnell implementiert. Kann ich keinen CORS Header setzten auf meinen Systemen ist der Proxy eine gute Alternative.
 Was die Sicherheit angeht sehe ich jedoch etwas Vorteile bei der Proxy Implementierung.