Wo Webentwickler das erste Mal SQL schreiben, passiert es: Sie bauen eine Abfrage aus Nutzereingaben zusammen. Injection steht in den OWASP Top 10 von 2021 auf Platz 3; Platz 1 ist Broken Access Control. Wer die Lücke nicht kennt, öffnet jedem Angreifer die Tür zur Datenbank.
Injection heißt: Eingabedaten werden vom Programm als Befehl gelesen, weil Befehl und Daten im selben Textfluss stehen. SQL ist die Abfragesprache relationaler Datenbanken, mit Befehlen wie SELECT, INSERT, UPDATE und DELETE. Vermischt eine Abfrage SQL-Text und Nutzereingabe, kann die Datenbank sie nicht mehr unterscheiden.
Ein Login-Formular zeigt es. Der Code hängt den Benutzernamen per String-Verkettung an einen SELECT-String an. Die Eingabe mit einem Hochkomma und einem immer wahren Vergleich beendet den Originaltext und fügt einen eigenen SQL-Befehl hinzu. Die Passwortprüfung fällt, weil die Bedingung für jede Zeile wahr ist.
Die wirksamste Abwehr sind parametrisierte Abfragen, auch Prepared Statements genannt. Der SQL-Text enthält nur Fragezeichen als Ersatz für die Werte. Diese Werte werden getrennt vom SQL-Text übertragen, deshalb liest die Datenbank sie nie als Teil des Befehls. String-Verkettung ist nie sicher, auch nicht mit Filtern.
Verwechselt wird Injection oft mit Validierungsfehlern. Validierung prüft, ob Daten erlaubt sind. Separation sorgt dafür, dass Daten überhaupt nicht als Befehl gelesen werden können.
Injection ist nicht ein Problem falscher Daten, sondern der Vermischung von Befehl und Daten. Getrennt übertragene Werte scheiden beides – und dokumentieren nebenbei, welche Eingaben Daten sind.
📌 SQL-Injection · String-Verkettung · Prepared Statements
📚 Webentwicklung von Grund auf · Tag 6 von 30
#WebSecurity #webdev #NodeJS
Genau hier werden Eingabe und Ausgabe verwechselt. Viele prüfen, was hereinkommt, und halten die Seite dann für sicher. Ob Cross-Site Scripting (XSS) entsteht, entscheidet sich aber erst bei der Ausgabe.
Eingabefilterung prüft Daten, bevor der Server sie annimmt. Sie blockt etwa HTML-Zeichen im Kommentarfeld oder lehnt verdächtige Werte ab. Das Problem: Ein Filter kann nie alle Fälle kennen. Gibt jemand statt eines Kommentars ein script-Element mit JavaScript ein, entscheidet nicht der Filter, sondern erst die Ausgabe, ob es läuft.
Ausgabekodierung, auch Escaping genannt, greift dort, wo der Schaden entsteht. Sie ersetzt Zeichen mit Sonderbedeutung durch ungefährliche Kürzel. So wird < zu <, und der Browser zeigt spitze Klammern als Text an, statt sie als Tag zu lesen.
Filterung schätzt die Eingabe ein, Kodierung entschärft die Ausgabe. Weil dieselbe Eingabe in HTML-Text, Attribut, URL oder JavaScript-String je eigene Kodierungsregeln braucht, schützt nur kontextgerechtes Escaping an jedem Ausgabeort.
„Faustregel:" Eingaben validieren, um fehlerhafte Daten abzuweisen; für Sicherheit aber jede Ausgabe im passenden Kontext kodieren. Eine Content Security Policy, die vorgibt, von wo Skripte geladen dürfen, ergänzt das als zweite Verteidigungslinie.
XSS heißt nicht, dass jemand den Server knackt, sondern dass fremdes Skript im Browser der Opfer läuft – etwa mit deren Sitzungs-Cookies. Geschlossen wird die Lücke durch Kodierung am Ausgabeort, nicht durchs Filtern allein.
📌 Eingabe prüfen · Ausgabe kodieren · Kontext beachten
📚 Webentwicklung von Grund auf · Tag 5 von 30
#WebSecurity #webdev #javascript
Einem JWT begegnet man als Header der Form Authorization: Bearer, gefolgt vom Token, sobald eine API nach einem Login verlangt. Wer nicht weiß, was darin steht, legt unbemerkt Geheimnisse in den Payload offen.
Ein JWT ist ein Zugriffstoken aus drei Teilen, die mit Punkten verbunden sind: Header, Payload und Signatur. Alle drei sind Base64url-kodiert, also in druckbare Zeichen umgewandelt – das ist eine Kodierung, keine Verschlüsselung, und lässt sich ohne Schlüssel sofort zurückübersetzen.
Der Header ist ein JSON-Objekt mit Metadaten, vor allem dem Signaturverfahren im Feld alg. Die Payload ist ebenfalls JSON und enthält Claims, also Angaben wie Nutzerkennung oder Ablaufzeitpunkt. Der Claim exp gibt den Ablaufzeitpunkt als Unix-Zeit an, iat die Ausstellungszeit. Das Token an den Punkten trennen und die ersten beiden Teile zurückübersetzen: Nutzerkennung und Rollen stehen im Klartext. Die Signatur entsteht, weil der Aussteller Header und Payload mit einem nur ihm bekannten Schlüssel signiert. Ändert jemand die Payload, passt die Signatur nicht mehr – der Prüfer verwirft das Token.
Verwechselt wird das mit Verschlüsselung: Die Signatur macht kein Geheimnis aus dem Inhalt, sie belegt nur, dass genau dieser Aussteller genau diesen Inhalt erzeugt hat. Der Prüfer gibt nach RFC 8725 den Algorithmus fest vor und lehnt none ab.
Ein JWT ist kein Briefumschlag, sondern eine unterschriebene Postkarte: Inhalt offen lesbar, Absender beglaubigt. Deshalb gehören Passwörter und Geheimnisse nie in die Payload.
📌 Base64url · Claims · Signatur
📚 Webentwicklung von Grund auf · Tag 4 von 30
#WebSecurity #api #backend #webdev
Erklärt wird hier, wie eine authentifizierte Anfrage über das Netz läuft. Die Situation kennt jeder, der einmal eine API oder eine App mit Login benutzt hat.
Zuerst sendet der Client beim Einloggen seine Zugangsdaten, etwa E-Mail und Passwort, an den Server. Der prüft sie und stellt im Erfolgsfall ein Zugriffstoken aus, also eine Art digitale Eintrittskarte. HTTP selbst ist zustandslos: Jede Anfrage steht für sich, und der Server erkennt einen Nutzer nur wieder, wenn Daten wie ein Token mitgesendet werden.
Dann schickt der Client das Token bei jeder weiteren Anfrage im Authorization-Header mit, nach dem Schema „Authorization: Bearer“ gefolgt vom Tokenwert. Bearer ist Englisch für Inhaber. Der Server prüft nur, ob ein gültiges Token vorliegt – nicht, wer die Anfrage eigentlich stellt.
Danach entscheidet der Server allein auf Basis dieses Headers. Wer das Token besitzt, darf damit arbeiten, wie ein Gast mit Garderobenmarke. Ein solches Token ist meist zeitlich begrenzt; anders als ein Passwort läuft es also bald ab.
Zuletzt kommt die Übertragung: Sie muss über TLS laufen, also über die verschlüsselte Variante von HTTP namens HTTPS. Nur dann ist der Header vor dem Mitlesen geschützt. Über offenes HTTP kann jeder im Netzpfad, etwa in einem offenen WLAN, das Token abfangen und anschließend selbst verwenden. Der Besitz allein reicht ja aus.
Bearer heißt nicht „berechtigte Person“, sondern „Inhaber“. Ohne HTTPS trägt jede Anfrage das Token offen im Klartext mit.
📌 Bearer-Token · Authorization-Header · TLS
📚 Webentwicklung von Grund auf · Tag 4 von 30
#HTTP #WebSecurity #webdev #api
Beide laufen direkt hintereinander ab und landen oft in einem Topf namens „Login“. Dabei können sie unabhängig scheitern – und der Server antwortet dann mit unterschiedlichen Fehlercodes.
Bei der Authentifizierung gleicht der Server eine Angabe wie Passwort oder Token (ein Ausweis-Datenstück aus Buchstaben und Zahlen) mit seinen gespeicherten Daten ab. Daraus ermittelt er eine Identität, etwa die Nutzer-ID 42. Das ist der erste Schritt: Ohne feststehende Identität kann keine Rechteprüfung folgen.
Bei der Autorisierung prüft der Server für jede einzelne Anfrage, ob diese Identität die angefragte Aktion ausführen darf. Konkret: Nutzer 42 meldet sich mit Passwort an. Beim Aufruf von DELETE /artikel/7 prüft der Server zusätzlich, ob 42 die Rolle Redakteur hat. Ohne diese Rolle folgt der Statuscode 403.
Scheitert die Authentifizierung, antwortet der Server mit 401 (nicht authentifiziert). Scheitert nur die Autorisierung, folgt 403 (verboten) – die Identität ist bekannt, aber die Rechte fehlen.
Weil HTTP zustandslos ist – jede Anfrage steht für sich –, muss jede Anfrage die Identität erneut belegen, etwa durch ein Cookie, ein Name-Wert-Paar, das der Server setzt und der Browser zurücksendet, mit einer Sitzungs-ID als Verweis auf die serverseitig gespeicherte Anmeldung. Der Server authentifiziert also bei jeder Anfrage von Neuem. Und weil der Browser-Seite nicht vertraut werden kann, gehört jede dieser Prüfregeln zusätzlich ins Backend.
Faustregel: Erst die Identität feststellen, dann je Anfrage die Rechte prüfen – beide Prüfungen niemals nur im Browser.
Authentifizierung heißt nicht „eingeloggt und alles erlaubt“, sondern nur „erkannt“. Was jemand darf, entscheidet die Autorisierung – separat und bei jedem Zugriff neu.
📌 Identität · Rechte · 401 vs. 403
📚 Webentwicklung von Grund auf · Tag 4 von 30
#WebSecurity #backend #HTTP
Der Browser vergleicht bei jeder Antwort, ob sie von derselben Origin (der Kombination aus Schema, Host und Port) stammt wie die Seite, deren JavaScript sie lesen will. Das begegnet Einsteigern sofort, sobald Frontend und Backend getrennt laufen.
Zuerst lädt der Browser die Seite, etwa eine gewöhnliche Dokumentseite unter https. JavaScript auf dieser Seite ruft dann Daten ab, zum Beispiel von einer API unter einem anderen Host. Die Anfrage geht ganz normal raus; der Server antwortet, ohne zu prüfen, wer sie liest.
Dann zählt der Browser Schema, Host und Port, nicht den Pfad: Zwei Adressen wie /seite1 und /api/daten auf demselben Host mit demselben Port teilen sich eine Origin. Aber ein API-Host und ein www-Host derselben Domain sind verschiedene Hosts, und derselbe Host mit abweichendem Port wie 8443 ist eine andere Origin. Ein fehlender Port bedeutet den Standardport, also 443 bei https.
Danach hält der Browser die Antwort zurück, wenn die Origins verschieden sind. Der Browser schickt die Anfrage, gibt die Antwort aber nicht an JavaScript weiter, solange der Server sie nicht per CORS (einem Header-Mechanismus zur Freigabe) erlaubt. Bilder und Stylesheets fremder Origins erscheinen trotzdem, weil kein Skript ihren Inhalt liest.
Zuletzt gilt die Grenze nur im Browser. curl (ein Kommandozeilenwerkzeug für HTTP-Anfragen) sendet und liest ohne Sandbox alles; die Same-Origin-Policy schützt den Server nicht, nur die Seite im Browser.
Gleiche Origin heißt also nicht gleicher Host, und fremde Origin heißt nicht blockierte Anfrage – sondern unlesbare Antwort.
📌 Origin · Same-Origin-Policy · CORS
📚 Webentwicklung von Grund auf · Tag 3 von 30
#WebSecurity #webdev #javascript
Die Verwechslung ist naheliegend: URL und Origin sehen fast gleich aus, und die Origin ist der vordere Teil der URL. Wer beides gleichsetzt, wundert sich später, warum eine Anfrage blockiert wird.
Die URL (Uniform Resource Locator, eine komplette Webadresse) besteht aus Schema, Host, optionalem Port, Pfad, optionaler Query nach dem Fragezeichen und optionalem Fragment nach der Raute. Sie benennt eine konkrete Ressource wie eine Seite oder ein Bild. Schema https, Pfad /orders und Query id=7 zeigen vier dieser Bestandteile in einer Adresse.
Die Origin besteht nur aus Schema, Host und Port; der Rest zählt nicht mit. Der Browser vergleicht zwei Adressen, indem er genau diese drei Teile gegenüberstellt; eine Abweichung ergibt eine andere Origin. Zwei Adressen mit gleichem Host sind darum verschieden, sobald der Port abweicht (Standardport gegenüber 8443) oder das Schema (http gegenüber https).
Pfad, Query und Fragment dürfen beliebig abweichen, ohne die Origin zu ändern. Ein fehlender Port bedeutet zudem den Standardport des Schemas – bei https der 443, bei http der 80; eine Adresse mit und ohne diese 443 hat dieselbe Origin.
Faustregel: Beim Aufrufen einer Ressource die volle URL angeben, beim Prüfen von Herkunft und Zugriff nur Schema, Host und Port vergleichen. Auch Unterdomänen wie api und www desselben Namens sind verschiedene Hosts und damit verschiedene Origins; die Same-Origin-Policy, eine Browser-Regel, gibt JavaScript nur Antworten derselben Origin frei, fremde Origins brauchen eine CORS-Freigabe des Servers.
Die Origin heißt nicht Adresse, sondern Herkunft: Schema, Host und Port – und sonst nichts.
📌 Origin · Same-Origin-Policy · URL-Bestandteile
📚 Webentwicklung von Grund auf · Tag 3 von 30
#webdev #WebSecurity #HTTP
Der Transporter fährt durch offenes Gelände. Beobachter sehen den Wagen, erkennen aber keinen Inhalt. Wird eine Kiste unterwegs aufgebrochen, fällt der Eingriff sofort auf.
Technisch ist HTTPS HTTP über TLS, also das normale Web-Protokoll über eine verschlüsselte Verbindung. Der TLS-Handshake, die Verbindungsaufnahme, einigt sich auf einen Schlüssel und prüft das Zertifikat: ein digitaler Ausweis, den eine Zertifizierungsstelle als anerkannte Vertrauensinstanz signiert hat. Der Browser vergleicht den Domainnamen im Zertifikat mit dem Host der URL, also dem Domainnamen in der Adresse; danach laufen alle Anfragen und Antworten als Chiffretext, also unlesbarer Geheimtext.
Beim Aufruf ist das Schema der URL der Teil vor dem Doppelpunkt: Es sagt dem Client, welches Protokoll er nutzt. Aus http wird damit https: verschlüsselte Übertragung mit Geheimhaltung, statt Klartext. Das Schloss im Browser bedeutet also nicht „sicherer Server“, sondern belegt nur zwei Eigenschaften des Wegs: verschlüsselte Übertragung und geprüfte Identität.
Das Bild hat eine Grenze: Der Transporter schützt weder das Lager noch die Ware nach der Abladung. Nach dem Entschlüsseln liegen die Daten auf dem Server im Klartext vor; Angriffe im Backend liegen außerhalb der Zuständigkeit der Verbindung.
HTTPS heißt nicht „dieser Server ist gut“, sondern „dieser Weg ist verschlüsselt, und dieser Ausweis passt zur Domain“.
📌 TLS-Verschlüsselung · Zertifikat · Schloss-Symbol
📚 Webentwicklung von Grund auf · Tag 3 von 30
#WebSecurity #HTTP #webdev
Nach dem Schließen und Wiederöffnen des Browsers ist der Nutzer abgemeldet. Beim Klick durch die Seiten ist dagegen alles normal.
Ein Cookie ist ein Name-Wert-Paar, das der Server mit dem Header Set-Cookie setzt und das der Browser bei passenden Anfragen im Header Cookie zurücksendet. Header sind Zeilen, die bei HTTP Daten zwischen Server und Browser austauschen; HTTP selbst ist zustandslos, jede Anfrage steht für sich. Der Server erkennt Nutzer also nur anhand mitgesendeter Werte wie Cookies, etwa als Schlüssel in seiner Session-Tabelle. Ohne Expires und Max-Age entsteht ein Sitzungscookie: Der Browser verwirft es beim Schließen, kann die Sitzung aber wiederherstellen. Mit Expires, einem konkreten Ablaufdatum, oder Max-Age, einer Lebensdauer in Sekunden, entsteht ein dauerhaftes Cookie, das Neustarts überlebt; ist beides gesetzt, gewinnt Max-Age.
Set-Cookie: sessionid=abc123; Path=/; HttpOnly setzt ein Sitzungscookie, das beim Beenden des Browsers verschwindet. Fügt man Max-Age=3600 an, bleibt es eine Stunde lang bestehen. Der Nutzer bleibt dann auch nach einem Neustart angemeldet – bis die Frist abläuft.
So prüft man es: Im Browser-DevTools unter Application die Cookies der Seite ansehen und die Spalten Expires und Max-Age prüfen. Sitzungscookies zeigen dort „Session“ statt eines Datums.
Sitzungscookie meint hier nur die Speicherung im Browser, nicht eine Server-Session; auch ein dauerhaftes Cookie kann einen Sitzungsschlüssel tragen, den der Server jederzeit für ungültig erklärt. Expires und Max-Age sind also die Schalter für die Lebensdauer, nicht für den Inhalt.
📌 Sitzungscookie · Max-Age · dauerhaftes Cookie
📚 Webentwicklung von Grund auf · Tag 3 von 30
#webdev #HTTP #WebSecurity #backend
Beide beginnen mit 4 und gehören zur Klasse 4xx, also Fehlern in der Anfrage des Clients. Der Rumpf der Nummer unterscheidet dann, was genau schiefging.
401 Unauthorized heißt trotz seines Namens „nicht angemeldet“. Der Server kennt die Identität des Anfragenden nicht oder hält den Nachweis für ungültig; Authentifizierung ist genau dieses Prüfen, wer jemand ist, etwa per Login-Token. Deshalb schickt der Server im WWW-Authenticate-Header mit, welche Anmeldeverfahren er akzeptiert. Beispiel: Ein Aufruf von DELETE /users/42 ohne Token bekommt 401 samt Hinweis auf das Login-Verfahren.
403 Forbidden heißt „nicht erlaubt“. Der Server weiß zwar, wer anfragt, aber diese Person darf die Ressource nicht sehen; Autorisierung ist dieses Prüfen, was jemand tun darf. Ein erneutes Anmelden hilft nicht. Beispiel: Eine Nutzerin mit gültigem Token, aber ohne Admin-Rolle, ruft DELETE /users/42 auf und bekommt 403.
Der entscheidende Unterschied liegt also in der Identität: Bei 401 fehlt der Nachweis, wer anfragt, bei 403 ist der Nachweis da, aber die Erlaubnis fehlt. Verbreiteter Irrtum: Ein falsches Passwort gehört zur fehlenden Authentifizierung und damit zu 401, nicht zu 403.
Faustregel: Im eigenen Backend bei fehlendem oder ungültigem Token 401 zurückgeben, bei gültigem Token ohne ausreichende Rolle 403. Manche Backends antworten auf fehlende Berechtigung mit 404, um die Existenz einer Ressource nicht zu verraten; fürs Lernen genügt die klare Trennung.
401 heißt nicht „verboten“, sondern „erst anmelden“ – 403 heißt „angemeldet, aber nicht erlaubt“.
📌 401 = Anmeldung fehlt · 403 = Erlaubnis fehlt · WWW-Authenticate-Header
📚 Webentwicklung von Grund auf · Tag 2 von 30
#HTTP #REST #WebSecurity