Ein umfassender Leitfaden zum Verständnis und zur Implementierung von Cross-Origin Resource Sharing (CORS) für die sichere JavaScript-Kommunikation zwischen verschiedenen Domains.
Implementierung der Cross-Origin-Sicherheit: Best Practices für die JavaScript-Kommunikation
Im heutigen vernetzten Web müssen JavaScript-Anwendungen häufig mit Ressourcen von verschiedenen Ursprüngen (Domains, Protokollen oder Ports) interagieren. Diese Interaktion wird durch die Same-Origin Policy des Browsers geregelt, ein entscheidender Sicherheitsmechanismus, der entwickelt wurde, um zu verhindern, dass bösartige Skripte auf sensible Daten über Domaingrenzen hinweg zugreifen. Legitime ursprungsübergreifende Kommunikation ist jedoch oft notwendig. Hier kommt Cross-Origin Resource Sharing (CORS) ins Spiel. Dieser Artikel bietet einen umfassenden Überblick über CORS, seine Implementierung und Best Practices für eine sichere ursprungsübergreifende Kommunikation in JavaScript.
Die Same-Origin Policy verstehen
Die Same-Origin Policy (SOP) ist ein grundlegendes Sicherheitskonzept in Webbrowsern. Sie schränkt Skripte, die von einem Ursprung ausgeführt werden, darin ein, auf Ressourcen von einem anderen Ursprung zuzugreifen. Ein Ursprung wird durch die Kombination aus Protokoll (z. B. HTTP oder HTTPS), Domainname (z. B. example.com) und Portnummer (z. B. 80 oder 443) definiert. Zwei URLs haben nur dann denselben Ursprung, wenn alle drei Komponenten exakt übereinstimmen.
Zum Beispiel:
http://www.example.comundhttp://www.example.com/path: Gleicher Ursprunghttp://www.example.comundhttps://www.example.com: Unterschiedlicher Ursprung (unterschiedliches Protokoll)http://www.example.comundhttp://subdomain.example.com: Unterschiedlicher Ursprung (unterschiedliche Domain)http://www.example.com:80undhttp://www.example.com:8080: Unterschiedlicher Ursprung (unterschiedlicher Port)
Die SOP ist eine entscheidende Verteidigung gegen Cross-Site-Scripting-Angriffe (XSS), bei denen bösartige Skripte, die in eine Website eingeschleust werden, Benutzerdaten stehlen oder nicht autorisierte Aktionen im Namen des Benutzers durchführen können.
Was ist Cross-Origin Resource Sharing (CORS)?
CORS ist ein Mechanismus, der HTTP-Header verwendet, um Servern zu ermöglichen, anzugeben, welchen Ursprüngen (Domains, Schemata oder Ports) der Zugriff auf ihre Ressourcen gestattet ist. Es lockert im Wesentlichen die Same-Origin Policy für spezifische ursprungsübergreifende Anfragen und ermöglicht eine legitime Kommunikation, während es gleichzeitig vor bösartigen Angriffen schützt.
CORS funktioniert durch das Hinzufügen neuer HTTP-Header, die die erlaubten Ursprünge und die für ursprungsübergreifende Anfragen zulässigen Methoden (z. B. GET, POST, PUT, DELETE) angeben. Wenn ein Browser eine ursprungsübergreifende Anfrage stellt, sendet er einen Origin-Header mit der Anfrage. Der Server antwortet mit dem Access-Control-Allow-Origin-Header, der den oder die erlaubten Ursprünge angibt. Wenn der Ursprung der Anfrage mit dem Wert im Access-Control-Allow-Origin-Header übereinstimmt (oder wenn der Wert * ist), erlaubt der Browser dem JavaScript-Code, auf die Antwort zuzugreifen.
Wie CORS funktioniert: Eine detaillierte Erklärung
Der CORS-Prozess umfasst typischerweise zwei Arten von Anfragen:
- Einfache Anfragen (Simple Requests): Dies sind Anfragen, die bestimmte Kriterien erfüllen. Wenn eine Anfrage diese Bedingungen erfüllt, sendet der Browser die Anfrage direkt.
- Preflight-Anfragen (Preflighted Requests): Dies sind komplexere Anfragen, bei denen der Browser zuerst eine „Preflight“-OPTIONS-Anfrage an den Server senden muss, um festzustellen, ob die eigentliche Anfrage sicher gesendet werden kann.
1. Einfache Anfragen (Simple Requests)
Eine Anfrage gilt als „einfach“, wenn sie alle folgenden Bedingungen erfüllt:
- Die Methode ist
GET,HEADoderPOST. - Wenn die Methode
POSTist, ist derContent-Type-Header einer der folgenden: application/x-www-form-urlencodedmultipart/form-datatext/plain- Es werden keine benutzerdefinierten Header gesetzt.
Beispiel für eine einfache Anfrage:
GET /resource HTTP/1.1
Origin: http://www.example.com
Beispiel für eine Serverantwort, die den Ursprung erlaubt:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Content-Type: application/json
{
"data": "Einige Daten"
}
Wenn der Access-Control-Allow-Origin-Header vorhanden ist und sein Wert mit dem Ursprung der Anfrage übereinstimmt oder auf * gesetzt ist, erlaubt der Browser dem Skript, auf die Antwortdaten zuzugreifen. Andernfalls blockiert der Browser den Zugriff auf die Antwort, und eine Fehlermeldung wird in der Konsole angezeigt.
2. Preflight-Anfragen (Preflighted Requests)
Eine Anfrage gilt als „Preflighted“, wenn sie die Kriterien für eine einfache Anfrage nicht erfüllt. Dies tritt typischerweise auf, wenn die Anfrage eine andere HTTP-Methode verwendet (z. B. PUT, DELETE), benutzerdefinierte Header setzt oder einen anderen Content-Type als die erlaubten Werte verwendet.
Bevor die eigentliche Anfrage gesendet wird, sendet der Browser zuerst eine OPTIONS-Anfrage an den Server. Diese „Preflight“-Anfrage enthält die folgenden Header:
Origin: Der Ursprung der anfragenden Seite.Access-Control-Request-Method: Die HTTP-Methode, die in der eigentlichen Anfrage verwendet wird (z. B.PUT,DELETE).Access-Control-Request-Headers: Eine durch Kommas getrennte Liste der benutzerdefinierten Header, die in der eigentlichen Anfrage gesendet werden.
Beispiel für eine Preflight-Anfrage:
OPTIONS /resource HTTP/1.1
Origin: http://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header, Content-Type
Der Server muss auf die OPTIONS-Anfrage mit den folgenden Headern antworten:
Access-Control-Allow-Origin: Der Ursprung, der die Anfrage stellen darf (oder*, um jeden Ursprung zu erlauben).Access-Control-Allow-Methods: Eine durch Kommas getrennte Liste der HTTP-Methoden, die für ursprungsübergreifende Anfragen erlaubt sind (z. B.GET,POST,PUT,DELETE).Access-Control-Allow-Headers: Eine durch Kommas getrennte Liste der benutzerdefinierten Header, die in der Anfrage gesendet werden dürfen.Access-Control-Max-Age: Die Anzahl der Sekunden, für die die Preflight-Antwort vom Browser zwischengespeichert werden kann.
Beispiel für eine Serverantwort auf eine Preflight-Anfrage:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header, Content-Type
Access-Control-Max-Age: 86400
Wenn die Antwort des Servers auf die Preflight-Anfrage anzeigt, dass die eigentliche Anfrage erlaubt ist, sendet der Browser die eigentliche Anfrage. Andernfalls blockiert der Browser die Anfrage und zeigt eine Fehlermeldung an.
Implementierung von CORS auf der Serverseite
CORS wird hauptsächlich auf der Serverseite durch Setzen der entsprechenden HTTP-Header in der Antwort implementiert. Die spezifischen Implementierungsdetails variieren je nach verwendeter serverseitiger Technologie.
Beispiel mit Node.js und Express:
const express = require('express');
const cors = require('cors');
const app = express();
// CORS für alle Ursprünge aktivieren
app.use(cors());
// Alternativ, CORS für bestimmte Ursprünge konfigurieren
// const corsOptions = {
// origin: 'http://www.example.com'
// };
// app.use(cors(corsOptions));
app.get('/resource', (req, res) => {
res.json({ message: 'Dies ist eine CORS-fähige Ressource' });
});
app.listen(3000, () => {
console.log('Server lauscht auf Port 3000');
});
Die cors-Middleware vereinfacht das Setzen von CORS-Headern in Express. Sie können CORS für alle Ursprünge mit cors() aktivieren oder es für bestimmte Ursprünge mit cors(corsOptions) konfigurieren.
Beispiel mit Python und Flask:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route("/resource")
def hello():
return {"message": "Dies ist eine CORS-fähige Ressource"}
if __name__ == '__main__':
app.run(debug=True)
Die flask_cors-Erweiterung bietet eine einfache Möglichkeit, CORS in Flask-Anwendungen zu aktivieren. Sie können CORS für alle Ursprünge aktivieren, indem Sie app an CORS() übergeben. Die Konfiguration für bestimmte Ursprünge ist ebenfalls möglich.
Beispiel mit Java und Spring Boot:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/resource")
.allowedOrigins("http://www.example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("Content-Type", "X-Custom-Header")
.allowCredentials(true)
.maxAge(3600);
}
}
In Spring Boot können Sie CORS mit einem WebMvcConfigurer konfigurieren. Dies ermöglicht eine feingranulare Kontrolle über erlaubte Ursprünge, Methoden, Header und andere CORS-Einstellungen.
CORS-Header direkt setzen (Allgemeines Beispiel)
Wenn Sie kein Framework verwenden, können Sie Header direkt in Ihrem serverseitigen Code setzen (z. B. PHP, Ruby on Rails, etc.):
CORS Best Practices
Um eine sichere und effiziente ursprungsübergreifende Kommunikation zu gewährleisten, befolgen Sie diese Best Practices:
- Vermeiden Sie die Verwendung von
Access-Control-Allow-Origin: *in der Produktion: Allen Ursprüngen den Zugriff auf Ihre Ressourcen zu gestatten, kann ein Sicherheitsrisiko darstellen. Geben Sie stattdessen die exakten Ursprünge an, die erlaubt sind. - Verwenden Sie HTTPS: Verwenden Sie immer HTTPS sowohl für den anfragenden als auch für den bereitstellenden Ursprung, um Daten während der Übertragung zu schützen.
- Validieren Sie Eingaben: Validieren und bereinigen Sie immer Daten, die von ursprungsübergreifenden Anfragen empfangen werden, um Injection-Angriffe zu verhindern.
- Implementieren Sie eine ordnungsgemäße Authentifizierung und Autorisierung: Stellen Sie sicher, dass nur autorisierte Benutzer auf sensible Ressourcen zugreifen können.
- Zwischenspeichern Sie Preflight-Antworten: Verwenden Sie
Access-Control-Max-Age, um Preflight-Antworten zwischenzuspeichern und die Anzahl derOPTIONS-Anfragen zu reduzieren. - Erwägen Sie die Verwendung von Anmeldeinformationen (Credentials): Wenn Ihre API eine Authentifizierung mit Cookies oder HTTP-Authentifizierung erfordert, müssen Sie den
Access-Control-Allow-Credentials-Header auf dem Server auftrueund diecredentials-Option in Ihrem JavaScript-Code auf'include'setzen (z. B. bei Verwendung vonfetchoderXMLHttpRequest). Seien Sie bei der Verwendung dieser Option äußerst vorsichtig, da sie bei falscher Handhabung Sicherheitslücken schaffen kann. Außerdem kannAccess-Control-Allow-Originnicht auf „*“ gesetzt werden, wennAccess-Control-Allow-Credentialsauftruegesetzt ist. Sie müssen den oder die erlaubten Ursprünge explizit angeben. - Überprüfen und aktualisieren Sie die CORS-Konfiguration regelmäßig: Überprüfen und aktualisieren Sie Ihre CORS-Konfiguration regelmäßig, während sich Ihre Anwendung weiterentwickelt, um sicherzustellen, dass sie sicher bleibt und Ihren Anforderungen entspricht.
- Verstehen Sie die Auswirkungen verschiedener CORS-Konfigurationen: Seien Sie sich der Sicherheitsauswirkungen verschiedener CORS-Konfigurationen bewusst und wählen Sie die für Ihre Anwendung geeignete Konfiguration.
- Testen Sie Ihre CORS-Implementierung: Testen Sie Ihre CORS-Implementierung gründlich, um sicherzustellen, dass sie wie erwartet funktioniert und keine Sicherheitslücken einführt. Verwenden Sie die Entwicklertools des Browsers, um Netzwerkanfragen und -antworten zu überprüfen, und verwenden Sie automatisierte Testwerkzeuge, um das CORS-Verhalten zu verifizieren.
Beispiel: Verwendung der Fetch API mit CORS
Hier ist ein Beispiel, wie Sie die fetch-API verwenden, um eine ursprungsübergreifende Anfrage zu stellen:
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors', // Teilt dem Browser mit, dass dies eine CORS-Anfrage ist
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
}
})
.then(response => {
if (!response.ok) {
throw new Error('Netzwerkantwort war nicht in Ordnung');
}
return response.json();
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error('Es gab ein Problem mit dem Fetch-Vorgang:', error);
});
Die Option mode: 'cors' teilt dem Browser mit, dass es sich um eine CORS-Anfrage handelt. Wenn der Server den Ursprung nicht zulässt, blockiert der Browser den Zugriff auf die Antwort, und ein Fehler wird ausgelöst.
Wenn Sie Anmeldeinformationen (z. B. Cookies) verwenden, müssen Sie die credentials-Option auf 'include' setzen:
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors',
credentials: 'include', // Cookies in die Anfrage einschließen
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
// ...
});
CORS und JSONP
JSON with Padding (JSONP) ist eine ältere Technik zur Umgehung der Same-Origin Policy. Sie funktioniert, indem dynamisch ein <script>-Tag erstellt wird, das Daten von einer anderen Domain lädt. Obwohl JSONP in bestimmten Situationen nützlich sein kann, hat es erhebliche Sicherheitsbeschränkungen und sollte nach Möglichkeit vermieden werden. CORS ist die bevorzugte Lösung für die ursprungsübergreifende Kommunikation, da es einen sichereren und flexibleren Mechanismus bietet.
Wichtige Unterschiede zwischen CORS und JSONP:
- Sicherheit: CORS ist sicherer als JSONP, da es dem Server ermöglicht zu steuern, welche Ursprünge auf seine Ressourcen zugreifen dürfen. JSONP bietet keine Ursprungskontrolle.
- HTTP-Methoden: CORS unterstützt alle HTTP-Methoden (z. B.
GET,POST,PUT,DELETE), während JSONP nurGET-Anfragen unterstützt. - Fehlerbehandlung: CORS bietet eine bessere Fehlerbehandlung als JSONP. Wenn eine CORS-Anfrage fehlschlägt, liefert der Browser detaillierte Fehlermeldungen. Die Fehlerbehandlung bei JSONP beschränkt sich darauf, zu erkennen, ob das Skript erfolgreich geladen wurde.
Fehlerbehebung bei CORS-Problemen
CORS-Probleme können frustrierend zu debuggen sein. Hier sind einige gängige Tipps zur Fehlerbehebung:
- Überprüfen Sie die Browser-Konsole: Die Browser-Konsole liefert in der Regel detaillierte Fehlermeldungen zu CORS-Problemen.
- Inspizieren Sie Netzwerkanfragen: Verwenden Sie die Entwicklertools des Browsers, um die HTTP-Header sowohl der Anfrage als auch der Antwort zu inspizieren. Überprüfen Sie, ob die
Origin- undAccess-Control-Allow-Origin-Header korrekt gesetzt sind. - Überprüfen Sie die serverseitige Konfiguration: Überprüfen Sie Ihre serverseitige CORS-Konfiguration sorgfältig, um sicherzustellen, dass sie die richtigen Ursprünge, Methoden und Header zulässt.
- Leeren Sie den Browser-Cache: Manchmal können zwischengespeicherte Preflight-Antworten CORS-Probleme verursachen. Versuchen Sie, Ihren Browser-Cache zu leeren oder ein privates Browserfenster zu verwenden.
- Verwenden Sie einen CORS-Proxy: In einigen Fällen müssen Sie möglicherweise einen CORS-Proxy verwenden, um CORS-Beschränkungen zu umgehen. Seien Sie sich jedoch bewusst, dass die Verwendung eines CORS-Proxys Sicherheitsrisiken mit sich bringen kann.
- Suchen Sie nach Fehlkonfigurationen: Suchen Sie nach häufigen Fehlkonfigurationen wie einem fehlenden
Access-Control-Allow-Origin-Header, falschenAccess-Control-Allow-Methods- oderAccess-Control-Allow-Headers-Werten oder einem falschenOrigin-Header in der Anfrage.
Fazit
Cross-Origin Resource Sharing (CORS) ist ein wesentlicher Mechanismus zur Ermöglichung einer sicheren ursprungsübergreifenden Kommunikation in JavaScript-Anwendungen. Durch das Verständnis der Same-Origin Policy, des CORS-Workflows und der verschiedenen beteiligten HTTP-Header können Entwickler CORS effektiv implementieren, um ihre Anwendungen vor Sicherheitslücken zu schützen und gleichzeitig legitime ursprungsübergreifende Anfragen zuzulassen. Das Befolgen von Best Practices für die CORS-Konfiguration und die regelmäßige Überprüfung Ihrer Implementierung sind entscheidend für die Aufrechterhaltung einer sicheren und robusten Webanwendung.
Dieser umfassende Leitfaden bietet eine solide Grundlage zum Verständnis und zur Implementierung von CORS. Denken Sie daran, die offizielle Dokumentation und die Ressourcen für Ihre spezifische serverseitige Technologie zu konsultieren, um sicherzustellen, dass Sie CORS korrekt und sicher implementieren.