10.12. CORS und CSRF¶
CORS und CSRF sind die beiden browserseitigen Schutzmechanismen, die eine ans offene Internet angeschlossene Kamera neben HTTPS und Login benötigt. Jeder lässt sich in wenigen Zeilen einrichten. Die folgenden Abschnitte definieren den Begriff und zeigen die microdot-Integration.
10.12.1. Was CORS bewirkt¶
Cross-Origin Resource Sharing (CORS) ist der Browser-Mechanismus, mit dem ein Server explizit erlauben kann, dass bestimmte andere Origins seine Antworten lesen. Die standardmäßige Same-Origin-Policy des Browsers blockiert dieses Lesen: JavaScript auf https://example.com kann keine Antworten von https://yard-cam.example.com lesen, weil ein anderer Host als anderer Origin zählt. CORS ist die serverseitige Methode, um Ausnahmen für ausgewählte Gegenstellen zu gewähren.
Wenn das Dashboard von der Kamera selbst ausgeliefert wird, ist jede Anfrage same-origin, und CORS bewirkt nichts. Die Einrichtung ist wichtig, wenn das Dashboard woanders liegt – eine öffentliche URL wie https://app.example.com, die mit einer Kamera unter https://yard-cam.example.com kommuniziert:
from microdot.cors import CORS
cors = CORS(
app,
allowed_origins=['https://app.example.com'],
allow_credentials=True,
max_age=86400,
)
allowed_origins ist die Liste der Origins, die die Antworten der Kamera lesen dürfen. Der Origin des Dashboards und nur der Origin des Dashboards – nicht * – sodass eine Drittanbieterseite nicht versehentlich die Antworten der Kamera lesen kann.
allow_credentials=True erlaubt es Cross-Origin-Anfragen, das Session-Cookie einzuschließen, was das Dashboard benötigt, um über eine Origin-Grenze hinweg angemeldet zu bleiben.
max_age=86400 teilt dem Browser mit, dass er das Preflight-Ergebnis einen Tag lang zwischenspeichern darf. Browser senden eine zusätzliche OPTIONS-Anfrage vor jedem Cross-Origin-Aufruf, der andere Methoden als GET/HEAD/POST verwendet oder benutzerdefinierte Header sendet; max_age reduziert diesen Mehraufwand auf einen Preflight pro Tag und Route.
10.12.2. Was CSRF bewirkt¶
Cross-Site Request Forgery (CSRF) ist der Angriff, bei dem eine bösartige Seite den Browser des Nutzers dazu bringt, eine authentifizierte Anfrage an einen vertrauenswürdigen Server zu senden. Selbst mit aktiviertem CORS erreicht ein verstecktes <form> auf evil.com, das an https://yard-cam.example.com/config POSTet, die Kamera, und der Browser hängt das Session-Cookie der Kamera an – Cookies folgen dem Zielhost, nicht dem Origin der Seite, die die Anfrage stellt – sodass die Kamera den POST bereitwillig so verarbeitet, als käme er vom Besitzer.
Der CSRF-Schutz lehnt diese Anfragen ab. microdot.csrf.CSRF fügt eine Middleware hinzu, die den Sec-Fetch-Site-Header bei jeder zustandsändernden Anfrage prüft und alles ablehnt, was nicht als same-origin gekennzeichnet ist (oder von einem per CORS erlaubten Origin stammt):
from microdot.csrf import CSRF
CSRF(app, cors=cors)
Die Übergabe der cors-Instanz erlaubt es der Middleware, den erlaubten Origin des Dashboards bestandsgeschützt durchzulassen – die Kamera akzeptiert weiterhin Dashboard-POSTs, obwohl diese cross-origin sind.
Sec-Fetch-Site wird von modernen Browsern automatisch gesetzt; die Kamera muss clientseitig nichts tun. Für ältere Browser, die den Header nicht senden, ist die CORS-Erlaubnisliste die Fallback-Prüfung.
10.12.3. Webhooks ausnehmen¶
Wenn die Kamera einen Webhook-Endpunkt benötigt, um POSTs von einem Cloud-Drittanbieterdienst entgegenzunehmen – etwa einen Rückruf vom Archivanbieter – markieren Sie die Route mit @csrf.exempt, damit die Middleware sie durchlässt. Der Handler ist dafür verantwortlich, die Anfrage auf andere Weise zu verifizieren – üblicherweise ein Hash-based Message Authentication Code (HMAC) über die Nutzlast, berechnet mit einem Geheimnis, das die Kamera und der Dritte teilen, was beweist, dass die Anfrage von jemandem stammt, der das Geheimnis kannte. Die Hinterhofkamera hat nichts davon, aber der Decorator ist da, wenn Sie ihn brauchen.
10.12.4. Die Vier-Zeilen-Basiskonfiguration¶
Sobald HTTPS aktiviert ist, lautet der empfohlene Stack für jede ans Internet angeschlossene Kamera-Bereitstellung:
Session(app, secret_key=SECRET,
cookie_options={'http_only': True, 'secure': True})
login = Login()
cors = CORS(app, allowed_origins=[...], allow_credentials=True)
CSRF(app, cors=cors)
Session und Login weiter vorne im Kapitel, CORS und CSRF hier, HTTPS aus dem vorherigen Thema. Die vier Bausteine stapeln sich aufeinander und bleiben jeder Route aus dem Weg.
Die Kamera kann jetzt sicher ans offene Internet angeschlossen werden – HTTPS, Login, CSRF, CORS.