Cross-Origin-Kontrolle

CORS (Cross-Origin Resource Sharing) ist ein Mechanismus, der es Browsern ermöglicht, Anfragen an serverübergreifende Server zu stellen und so die Beschränkungen der Browser-Same-Origin-Policy zu umgehen.

Was ist die Same-Origin-Policy?

Die Same-Origin-Policy ist eine Sicherheitsfunktion von Browsern, die einschränkt, wie Dokumente oder Skripte von einem Ursprung mit Ressourcen eines anderen Ursprungs interagieren können. "Same Origin" bedeutet, dass Protokoll, Domain und Portnummer identisch sind.

Warum wird CORS benötigt?

CORS-Unterstützung ist erforderlich, wenn eine Frontend-Anwendung auf APIs eines anderen Ursprungs zugreifen muss. Beispiel:

  • Die Frontend-Anwendung ist unter https://frontend.com bereitgestellt
  • Der API-Dienst ist unter https://api.backend.com bereitgestellt

Ohne CORS verhindert der Browser, dass die Frontend-Anwendung auf den API-Dienst zugreift.

Wie CORS funktioniert

CORS implementiert die serverübergreifende Zugriffskontrolle durch eine Reihe von HTTP-Headern:

  • Einfache Anfragen: Werden direkt gesendet, wobei der Server über Antwort-Header steuert, ob der Zugriff erlaubt ist.
  • Preflight-Anfragen: Der Browser sendet zuerst eine OPTIONS-Anfrage, um den Server zu fragen, ob serverübergreifender Zugriff erlaubt ist, und sendet die eigentliche Anfrage erst nach Erhalt der Erlaubnis.

Da der Browser Preflight-Anfragen mit Method::OPTIONS sendet, ist es notwendig, solche Anfragen zu behandeln. Das CORS-Middleware muss zum Service hinzugefügt werden.

Verwendung von CORS in Salvo

Salvo bietet ein integriertes CORS-Middleware, das einfach konfiguriert und verwendet werden kann. Hier ist ein Beispielcodeausschnitt:

Warning

Hinweis: Das .hoop(cors)-Middleware wird auf den Service angewendet, nicht auf den Router. Dies stellt sicher, dass OPTIONS-Preflight-Anfragen automatisch behandelt werden.

let cors = Cors::new()
    .allow_origin(["http://127.0.0.1:8698", "http://localhost:8698"])
    .allow_methods(vec![Method::GET, Method::POST, Method::DELETE])
    .allow_headers("authorization")
    .into_handler();

// Backend-Router mit CORS-Schutz einrichten
let router = Router::with_path("hello").post(hello);
let service = Service::new(router).hoop(cors);

Beispielcode

main.rs
Cargo.toml
use salvo::cors::Cors;
use salvo::http::Method;
use salvo::prelude::*;

#[tokio::main]
async fn main() {
    // Initialize logging system
    tracing_subscriber::fmt().init();
    // Start both backend and frontend servers concurrently
    tokio::join!(backend_server(), frontend_server());
}

async fn backend_server() {
    // Handler that returns a simple message for CORS demonstration
    #[handler]
    async fn hello() -> &'static str {
        "hello, I am content from remote server."
    }

    // Configure CORS middleware with specific settings:
    // - Allow requests from localhost:8698
    // - Allow specific HTTP methods
    // - Allow authorization header
    let cors = Cors::new()
        .allow_origin(["http://127.0.0.1:8698", "http://localhost:8698"])
        .allow_methods(vec![Method::GET, Method::POST, Method::DELETE])
        .allow_headers("authorization")
        .into_handler();

    // Set up backend router with CORS protection
    let router = Router::with_path("hello").post(hello);
    let service = Service::new(router).hoop(cors);

    // Start backend server on port 5600
    let acceptor = TcpListener::new("0.0.0.0:5600").bind().await;
    Server::new(acceptor).serve(service).await;
}

async fn frontend_server() {
    // Handler that serves the HTML page with CORS test
    #[handler]
    async fn index() -> Text<&'static str> {
        Text::Html(HTML_DATA)
    }

    // Set up frontend router to serve the test page
    let router = Router::new().get(index);
    // Start frontend server on port 8698
    let acceptor = TcpListener::new("0.0.0.0:8698").bind().await;
    Server::new(acceptor).serve(router).await;
}

// HTML template with JavaScript code to test CORS
// Contains a button that triggers a POST request to the backend server
const HTML_DATA: &str = r#"
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Salvo Cors</title>
</head>
<body>
<button id="btn">Load Content</button>
<div id="content"></div>
<script>
document.getElementById("btn").addEventListener("click", function() {
    fetch("http://127.0.0.1:5600/hello", {method: "POST", headers: {authorization: "abcdef"}}).then(function(response) {
        return response.text();
    }).then(function(data) {
        document.getElementById("content").innerHTML = data;
    });
});
</script>
</body>
</html>
"#;

Hauptkonfigurationsoptionen

Das CORS-Middleware bietet verschiedene Konfigurationsoptionen:

  • Erlaubte Ursprünge: Steuert, welche Domänen auf Ressourcen zugreifen können.
  • Erlaubte Methoden: Gibt erlaubte HTTP-Methoden an (GET, POST, etc.).
  • Erlaubte Header: Gibt erlaubte Anfrage-Header an.
  • Verfügbar gemachte Header: Gibt an, welche Antwort-Header vom Client abgerufen werden können.
  • Anmeldedaten erlauben: Bestimmt, ob Anfragen Anmeldedaten wie Cookies enthalten dürfen.
  • Preflight-Anfrage-Cache-Zeit: Die Cache-Dauer für Preflight-Anfrageergebnisse.

Durch angemessene Konfiguration von CORS können Sie sowohl Sicherheit als auch die Fähigkeit gewährleisten, serverübergreifende Zugriffsanforderungen zu erfüllen.