Du kennst nun den SDL (Secure Development Lifecycle) als Rahmen für sichere Anwendungsentwicklung: Sicherheit in jeder Phase mitdenken. Aber was bedeutet das konkret beim Schreiben von Code? Welche Fehler passieren am häufigsten, und wie vermeidest du sie?
Die Antwort beginnt mit einer goldenen Regel, die sich durch die gesamte sichere Anwendungsentwicklung zieht und die du bereits kennen solltest:
Alles, was vom Nutzer kommt, ist nicht vertrauenswürdig.
Das gilt für Formulareingaben, URL-Parameter, Cookies, HTTP-Header, JSON-Felder, Datei-Uploads und Daten aus externen Schnittstellen. Es gilt sogar für Daten aus der eigenen Datenbank, wenn sie ursprünglich von Nutzern oder fremden Systemen stammen. In Modul B2 hast du Angriffe wie SQL-Injection, Cross-Site Scripting und Command Injection kennengelernt. Diese Angriffe funktionieren oft, weil eine Anwendung Eingaben als harmlos behandelt und sie später in einem gefährlichen Kontext verarbeitet.

„In der vorherigen Lektion haben wir bei der WAF gesehen, dass die eingehende Anfragen auf Angriffsmuster prüft. Wenn die WAF das schon macht, warum muss ich im Code nochmal validieren?“
„Weil das immer nur zusätzliche Schutzschichten sein sollten. Wenn dein Code selbst keine Validierung hat, hängt deine gesamte Sicherheit an einer einzelnen extra Komponente. Und was passiert, wenn jemand die Konfiguration ändert, oder das System ausfällt? Dann darf die Anwendung nicht ohne Hose dastehen…“

Input Validierung bedeutet, dass Eingaben geprüft werden, bevor die Anwendung sie verarbeitet. Die Anwendung entscheidet dabei, ob ein Wert zum erwarteten Format, Typ und fachlichen Kontext passt.
OWASP beschreibt Input Validation als Maßnahme, um sicherzustellen, dass nur korrekt geformte Daten in den Verarbeitungsfluss einer Anwendung gelangen: https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html
Für das NovaHealth-Patientenportal bedeutet das zum Beispiel:
Dabei gibt es wiederum zwei Ebenen:
Prüft die Form.
Prüft die Bedeutung im fachlichen Kontext.
Bei der Validierung gibt es ebenfalls verschiedene Strategien.
Ein gutes Beispiel ist ein Parameter, der nur wenige gültige Werte haben darf:
/laborergebnis?format=pdf
Die Anwendung unterstützt ausschließlich pdf und json.
Eine Blocklist wäre dann z.B.
Blockiere: xml, txt, exe, png, py, php, dll, yaml, ...
Es ist eigentlich klar, dass es wenig Sinn macht hier tausend verschiedene Dateiendungen zu blockieren. Denn dann kommt ein Angreifer und macht plötzlich: format=../../etc/passwd was eine völlig neue Schwachstelle aufreißt.
Allowlisting ist eigentlich immer die sicherere Strategie, weil nur das durchgelassen wird, was explizit bekannt ist und erwartet wird. Beim Blocklisting müssen alle möglichen Angriffsmuster bekannt sein und Angreifer finden ständig neue Wege solche Filter zu umgehen. Hier mal eine „kleine“ Liste mit möglichen Umgehungen von XSS Filtern: https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html

Stell dir das einfach so vor: Du kommst nicht in den Club, weil du nicht auf der Gästeliste stehst. In einem anderen Club gibt es nur eine Liste mit Hausverboten. Da der Türsteher dich nicht kennt, kommst du rein, machst Ärger und wirst rausgeworfen… aber der Schaden ist bereits verursacht.

„Ok, ich habe jetzt im Patientenportal eine Formularvalidierung eingebaut, die im Browser prüft, ob die E-Mail-Adresse gültig ist. Das sollte reichen, oder? Der Browser zeigt ja eine Fehlermeldung, wenn das Format nicht stimmt.“
„Das ist gut für die Benutzerfreundlichkeit, denn der Patient bekommt sofort Feedback. Aber es bietet keinen Schutz. Ein Angreifer kann den Browser komplett umgehen und Anfragen direkt an den Server senden. Zum Beispiel mit einem Tool wie Burp Suite oder einem einfachen curl-Befehl. Eine Client-Validierung existiert für Angreifer schlicht nicht.„

Die Regel lautet: Immer auf dem Server validieren. Client-seitige Validierung ist eine Komfortfunktion, keine Sicherheitsmaßnahme.
Bei der serverseitigen Validierung solltest du folgende Aspekte prüfen:
@, eine Postleitzahl hat fünf Ziffern (zumindest in DE (hier wirds wieder komplexer)).Authentizität und Autorisierung, also ob ein User überhaupt auf was genau zugreifen darf, wurde in vorigen Kapiteln bereits ausführlich thematisiert, fällt aber natürlich auch unter serverseitige Validierung.
Neben der Eingabevalidierung ist das Output Encoding entscheidend, um Angriffe wie XSS (Cross-Site Scripting) zu verhindern. Du hast XSS-Angriffe bereits in Modul B2 kennengelernt und in der Lektion über Webanwendungen die Abwehrseite gesehen. Output Encoding ist die Umsetzung im Code.
Output Encoding bedeutet, dass Daten vor der Ausgabe für den jeweiligen Kontext kodiert werden. OWASP beschriebt das ebenfalls in ihrem Cross-Site Scripting Prevention Cheat Sheet. Das ist wichtig, da ein und dieselbe Zeichenkette je nach Kontext unterschiedlich gefährlich sein kann:
<, > und & werden in ihre HTML-Entitäten umgewandelt (<, >, &). So kann ein Angreifer kein HTML einschleusen.%20).Ein konkretes Beispiel macht das deutlich:
Unsicheres Muster: die Benutzereingabe wird direkt in die HTML-Ausgabe eingefügt:
username = request.getParameter("name")
html = "<p>Willkommen, " + username + "</p>"
Wenn ein Angreifer als Namen <script>alert('XSS')</script> eingibt, wird dieses Script im Browser jedes Nutzers ausgeführt, der die Seite aufruft.
Sicheres Muster: die Ausgabe wird HTML-kodiert:
username = request.getParameter("name")
username = htmlEncode(username)
html = "<p>Willkommen, " + username + "</p>"
Jetzt wird <script> zu <script> und der Browser zeigt den Text an, statt ihn auszuführen.
Die htmlEncode() Funktion ist hier nur ein Beispiel. Jedes moderne Framework hat ein Äquivalent für diese Funktionalität, welches genutzt werden sollte. Bei Filtermechanismen sollte man nicht auf Eigenentwicklungen setzen, sondern auf bereits bewährte und robuste Implementierungen.
Einen weiteren wichtigen Schutzmechanismus kennst du schon aus der Lektion über Webanwendungen: Prepared Statements (auch parametrisierte Abfragen genannt). OWASP nennt Prepared Statements als zentrale Schutzmaßnahme gegen SQL-Injection, weil SQL-Code und Benutzerdaten getrennt bleiben: https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.md
Unsicher: die Benutzereingabe wird direkt in den SQL-String eingebaut:
query = "SELECT * FROM patients WHERE id = '" + userInput + "'"
Besser: die Benutzereingabe wird als Parameter übergeben:
query = "SELECT * FROM patients WHERE id = ?"
parameter = userInput
Beim Prepared Statement wird zuerst die SQL-Struktur festgelegt. Danach wird der Benutzerwert als Parameter übergeben. Die Datenbank behandelt ihn als Datenwert, nicht als SQL-Code.
Im Gegensatz zu Input Validierung, die prüft, ob eine Eingabe zulässig aussieht, sorgen Prepared Statements dafür, dass eine Eingabe die SQL-Logik nicht verändern kann. Wichtig: Beides gehört zusammen.
Im Kontext des SDL gehört Input Validierung und Output Encoding in mehrere Phasen.
In den Anforderung(en) wird festgelegt, welche Eingaben erlaubt sind und welche Daten geschützt werden müssen. Im Design wird entschieden, wo validiert wird und welche Komponenten Daten ausgeben. In der Implementation werden Validierung, Encoding und parametrisierte Abfragen umgesetzt. In der Verification wird geprüft, ob unerlaubte Eingaben abgelehnt und Ausgaben sicher dargestellt werden.

Input Validierung und Output Encoding sind die zwei Seiten derselben Medaille. Eingaben prüfen, bevor du sie verarbeitest. Ausgaben kodieren, bevor du sie anzeigst.
Lass uns schauen, ob du die Grundlagen der sicheren Datenverarbeitung drauf hast.