Im vorherigen Thema ging es darum, wie Anwendungen Eingaben prüfen und Ausgaben sicher kodieren. Aber es gibt weitere Aspekte sicherer Anwendungsentwicklung, die in der Praxis häufig unterschätzt werden: Wie eine Anwendung mit Fehlern umgeht und wie sie mit dem Arbeitsspeicher umgeht.
Stell dir vor, ein Entwickler bei NovaHealth testet das Laborergebnis-Modul und gibt absichtlich eine ungültige Patienten-ID ein. Im Browser erscheint folgende Meldung:
SQLException: Table 'production_db.patient_results' doesn't exist
at query SELECT * FROM patient_results WHERE patient_id = '12345'
Stack trace: com.novahealth.api.PatientController.getResults(PatientController.java:47)
Was sieht ein Entwickler? Einen nützlichen Debug-Hinweis. Was sieht ein Angreifer? Den Namen der Datenbank (production_db), den Namen der Tabelle (patient_results), die verwendete Programmiersprache (Java), die interne Klasse und Zeilennummer, und die Struktur der SQL-Abfrage. All das muss ein Angreifer normalerweise mühsam herausfinden, falls er es überhaupt schafft, und hier bekommt er es frei Haus.

„Klingt nach dem I in STRIDE, also ‚Information Disclosure‘. Aber wie schlimm ist das wirklich? Der Angreifer hat ja noch keinen Zugriff auf die Daten.“
„Noch nicht… aber du gibst ihm quasi eine Landkarte. Er weiß jetzt, wie die Datenbank heißt, welche Tabellen existieren und wie die Abfragen aufgebaut sind. Das ist genau die Information, die er für eine gezielte SQL-Injection braucht.“


„Die es natürlich nicht gibt, weil wir selbstverständlich Prepared Statements nutzen, haha. Aber ich verstehe worauf du hinaus willst.“
Fehlerbehandlung bedeutet, dass eine Anwendung kontrolliert auf unerwartete Situationen reagiert. Dazu gehören ungültige Eingaben, nicht erreichbare Datenbanken, fehlende Berechtigungen, abgelaufene Sessions oder interne Ausnahmen.
Das Grundprinzip sicherer Fehlerbehandlung besteht aus drei Regeln. Übrigens hat OWASP auch hier wieder mal ein Cheat Sheet:
Der letzte Punkt ist bei Sicherheitsentscheidungen besonders wichtig. Wenn eine Zugriffskontrolle, Rollenprüfung oder Token-Prüfung fehlschlägt, darf die Anwendung nicht aus Bequemlichkeit Zugriff gewähren.
Ein Beispiel aus NovaHealth:
Die Anwendung fragt den Authentifizierungsdienst:
"Darf Benutzer 123 dieses Laborergebnis sehen?"
...
...
...
Der Authentifizierungsdienst ist nicht erreichbar.

Unsichere Reaktion: Keine Antwort erhalten. Zugriff erlauben, damit der Ablauf nicht unterbrochen wird.

Sichere Reaktion: Keine Antwort erhalten. Zugriff verweigern und Fehler intern protokollieren.
⚠️ Fail Secure heißt nicht, dass jede Anwendung bei jedem kleinen Fehler komplett stoppen muss. Es heißt: Wenn eine Sicherheitsentscheidung nicht zuverlässig getroffen werden kann, darf nicht zugunsten des Zugriffs entschieden werden.

„Memory Sicherheit? Klingt nach Buffer Overflows… betrifft das moderne Anwendungen überhaupt noch?“
„Ja, Buffer Overflows sind eine der ältesten Schwachstellen-Klassen, aber sie verursachen bis heute viele der kritischen Sicherheitslücken.“

Um diese These zu untermauern: Microsoft und Google berichten, dass rund 70 Prozent ihrer kritischen Schwachstellen auf Memory-Safety-Probleme zurückgehen, wie hier oder hier nachzulesen.
Memory-Sicherheit bedeutet im Grunde, dass ein Programm den Arbeitsspeicher (RAM) nur so nutzt, wie es vorgesehen ist. Es darf nicht außerhalb spezieller für das Programm reservierter Bereiche schreiben, nicht auf bereits freigegebenen Speicher zugreifen und keine Speicherzustände erzeugen, die Sicherheitsprüfungen umgehen.

Dieses Thema ist besonders relevant bei Sprachen wie C und C++. Diese Sprachen geben Entwicklern viel Kontrolle über Speicher, was leistungsfähig sein kann, aber auch deutlich fehleranfälliger. Speichersichere Sprachen wie Rust, Go, Java, C# oder Python verhindern viele dieser Fehler durch Laufzeitprüfungen, automatische Speicherverwaltung oder strengere Sprachregeln.
Ein Buffer Overflow (Pufferüberlauf) passiert, wenn ein Programm mehr Daten in einen reservierten Speicherbereich schreibt, als dieser Bereich aufnehmen kann. Die überschüssigen Daten überschreiben benachbarte Speicherbereiche.
Ein einfaches Bild: Es gibt ein Formularfeld, das intern Platz für 20 Zeichen hat. Die Anwendung lässt aber zu 200 und mehr Zeichen hineinzuschreiben, ohne die Länge zu prüfen. Das kann dazuführen, dass diese Zeichen irgendwo hingeschrieben werden, wo die Anwendung keine Kontrolle mehr hat. In modernen Programmen ist das technisch nochmal deutlich komplexer, aber das Prinzip bleibt gleich: Daten landen dort, wo sie nicht hingehören.
Ein erfolgreicher Buffer Overflow kann dann dazu führen, dass ein Programm in einen unerwarteten Zustand kommt und abstürzt oder ein Angreifer es schafft den Programmablauf zu beeinflussen, in dem zum Beispiel eine bestimmte Adresse im Speicher gezielt überschrieben wird. Der folgende Comic stellt dies vereinfacht dar:

Quelle: https://hacking.art/articles/weirdstalker/
Andere Memory-Schwachstellen sind zum Beispiel Use-After-Free und Race Condition.
Use-After-Free bedeutet: Ein Programm greift auf Speicher zu, der eigentlich bereits wieder freigegeben wurde. Wenn dieser Speicher inzwischen anders genutzt wird, arbeitet das Programm mit Daten, die dort nicht mehr hingehören. Angreifer können solche Zustände ausnutzen, um den Ablauf des Programs zu manipulieren.
Race Condition bedeutet: Zwei Abläufe greifen gleichzeitig auf dieselbe Ressource zu, und das Ergebnis hängt vom Timing ab. Bei Speicher oder Sicherheitsprüfungen kann das gefährlich werden, wenn ein Wert zwischen Prüfung und Nutzung verändert wird.
Ein typisches Muster ist:
Das heißt zwischen der (erfolgreichen) Prüfung und der Nutzung findet eine Änderung statt, die die Prüfung hätte fehlschlagen lassen.
Solche Fehler werden oft als TOCTOU (Time-of-Check to Time-of-Use) bezeichnet. Zwischen Prüfung und Nutzung verändert sich der Zustand.

Memory-Schwachstellen sind so gefährlich, weil sie nicht nur Datenfehler verursachen, sondern den Programmablauf selbst beeinflussen können, was wiederum zur Ausführung von Schadcode führen kann.
Die stärkste Maßnahme ist, Speicherfehler gar nicht erst entstehen zu lassen. Für neue Projekte sollte geprüft werden, ob eine speichersichere Sprache geeignet ist. Das gilt besonders für Komponenten, die Daten aus unsicheren Quellen verarbeiten, zum Beispiel Parser, Upload-Verarbeitung oder Netzwerkdienste.
Mögliche Schutzmaßnahmen:
Rust, Go, Java, C# und Python nehmen Entwicklern viele gefährliche Speicheroperationen ab. Rust ist aktuell besonders gefragt, weil es im Gegensatz zu z.B. Python, was sehr langsam läuft, ähnlich schnell wie C und C++ ist.
Das Betriebssystem lädt Programme und Bibliotheken bei jedem Start an unterschiedliche Speicheradressen. Ein Angreifer kann dadurch schwerer vorhersagen, wo sich nutzbarer Code oder Daten im Speicher befinden.
Speicherbereiche, die nur Daten enthalten sollen, werden als nicht ausführbar markiert. Selbst wenn ein Angreifer Schadcode in einen Datenbereich schreibt, soll dieser Code nicht ausgeführt werden.
Das folgende Bild soll ASLR nochmal veranschaulichen.
Links wurde drei mal ein Kommando ausgeführt, welches die aktuellen Speicheradressen des Kommandos selbst anzeigt (des ausgeführten „awk“ Programms). Bei jeder Ausführung sind die Adressen unterschiedlich.
Anschließend wurde rechts im Bild ASLR deaktiviert, indem kernel.randomize_va_space von 2 auf 0 gesetzt wurde. Das vorige Kommando zeigt nun jedes Mal dieselben Speicheradressen.

Wenn ein Angreifer zum Beispiel mit einem Buffer Overflow versucht eine bestimtme Adresse gezielt zu überschreiben, kann ASLR durch die Randomisierung der Adressen dies deutlich erschweren. Auf modernen Systemen ist ASLR normalerweise per default aktiviert.
Ein weiterer wichtiger Schutz ist: Sensible Daten aus dem Speicher entfernen. Passwörter, Tokens und kryptografische Schlüssel sollten nicht länger im Speicher bleiben als nötig. Bei manchen Sprachen und Frameworks ist das schwer vollständig zu kontrollieren. Trotzdem sollten Entwickler vermeiden, sensible Daten unnötig zu kopieren, zu loggen oder langfristig im Speicher zu halten. Der PCI SSF Standard (eine Vorgabe wie Applikationen, die Kreditkartendaten verarbeiten, abgesichert werden müssen) schreibt sogar vor, Kreditkartendaten wie die PAN (Primary Account Number) unmittelbar nach einer Transaktion wieder aus dem Speicher der Applikation zu löschen.
💡 ASLR und DEP/NX sind wichtige Schutzmechanismen moderner Systeme. Sie machen Angriffe schwieriger, aber nicht unmöglich. Der bessere Ansatz ist, Speicherfehler durch sichere Sprache, sichere Bibliotheken und Tests früh zu vermeiden.
Ordne den Begriff der Bedeutung zu: