Texte zu Büchern

Secure by Design – Zusammenfassung und Einordnung

Dan Bergh Johnsson, Daniel Deogun, Daniel Sawano, Secure by Design (2019)

Lesestand

Einleitung

Sicherheit fehlt in der Reihe bisher fast völlig. Secure by Design füllt diese Lücke auf eine Weise, die zu den übrigen Büchern passt: Es behandelt Sicherheit nicht als Spezialgebiet mit eigenen Werkzeugen, sondern als Folge guten Designs. Wer Domänenkonzepte genau modelliert, Zustände unveränderlich hält und Fehler sauber behandelt, verhindert viele Sicherheitslücken, ohne an Sicherheit zu denken.

Autoren und Kontext

Dan Bergh Johnsson, Daniel Deogun und Daniel Sawano arbeiten als Berater beim schwedischen Unternehmen Omegapoint an der Schnittstelle von Softwareentwicklung und Sicherheit. Bergh Johnsson gehört zu denen, die schon früh vorschlugen, Sicherheit mit Domain-Driven Design zu verbinden; daraus entstand die Idee des Domänenprimitivs, eines Wertobjekts, das nur gültige Werte annehmen kann. Das Vorwort schrieb Daniel Terhorst-North. Das Buch erschien 2019 bei Manning und ist weiterhin in der ersten Auflage aktuell.

Die Kernthese

Entwickler können nicht ständig an Sicherheit denken; sie denken an Funktionen. Sicherheitsregeln, die man sich merken muss, werden vergessen. Gute Design-Gewohnheiten dagegen wendet man ohnehin an, und wenn sie richtig gewählt sind, entsteht Sicherheit nebenbei. Statt „denk an Eingabevalidierung“ heißt es dann: „Modelliere eine Kontonummer als eigenen Typ, der nur gültige Kontonummern zulässt.“

Aufbau

TeilKapitelInhalt
I. Einführung1–3warum Design für Sicherheit wichtig ist; eine Geschichte über negative Bestellmengen; Grundbegriffe aus DDD
II. Grundlagen4–10sichere Code-Konstrukte, Domänenprimitive, Integrität und Komplexität von Zustand, Sicherheit in der Auslieferungspipeline, sichere Fehlerbehandlung, Vorteile der Cloud-Denkweise
III. Anwendung11–14eine Geschichte über inkonsistente Modelle; Hinweise für Legacy-Code und Microservices; Grenzen des Ansatzes

Rund 400 Seiten, durchgehend mit Java-Beispielen.

Warum Design Sicherheit schafft

Sicherheit als Eigenschaft, nicht als Funktion

Viele Teams behandeln Sicherheit als eine Reihe von Funktionen: Anmeldung, Verschlüsselung, Rollen. Die Autoren betonen, dass Sicherheit eher eine Eigenschaft des ganzen Systems ist, ähnlich wie Leistung oder Wartbarkeit. Die klassischen Schutzziele sind:

SchutzzielFrage
VertraulichkeitSehen nur Berechtigte die Daten?
IntegritätSind die Daten korrekt und unverfälscht?
VerfügbarkeitIst das System erreichbar, wenn es gebraucht wird?

Viele Angriffe nutzen keine Lücken in Sicherheitsfunktionen, sondern Schwächen im gewöhnlichen Code: ungeprüfte Eingaben, unklare Zustände, Fehlermeldungen, die zu viel verraten.

Ein Beispiel aus dem ersten Kapitel

Die Autoren zeigen einen bekannten Angriff auf XML-Parser, bei dem ein winziges Dokument durch verschachtelte Entitäten beim Einlesen auf Gigabytes anwächst und den Server lahmlegt. Die übliche Antwort wäre eine Sicherheitsregel: „Konfiguriere deinen Parser sicher.“ Die Antwort aus Design-Sicht ist grundsätzlicher: Wer genau weiß, was ein gültiges Dokument in seiner Domäne ist, prüft Größe und Struktur, bevor der eigentliche Parser arbeitet, und schließt damit eine ganze Klasse von Angriffen aus.

Die Geschichte mit der negativen Menge

Ein Zwischenkapitel erzählt von einem Onlinebuchhändler. Die Bestellmenge war im Code eine gewöhnliche Ganzzahl. Ein Kunde bestellte eine negative Anzahl von Büchern, das System rechnete brav einen negativen Preis aus und schrieb den Betrag gut. Niemand hatte die Prüfung „Menge muss positiv sein“ vergessen, weil niemand je darüber nachgedacht hatte; „Menge“ war kein eigenes Konzept, nur eine Zahl.

Die Lehre: Hätte das System eine Menge als eigenen Typ mit klaren Regeln modelliert, wäre die negative Bestellung gar nicht darstellbar gewesen. Sicherheit entsteht hier nicht durch zusätzliche Prüfungen, sondern durch ein tieferes Modell.

Einordnung

Das ist Evans‘ Gedanke der tiefen Modellierung aus Sicherheitssicht. Bloch fordert in Effective Java dasselbe auf Code-Ebene: Klassen sollen nur gültige Zustände zulassen. Neu ist die Konsequenz: Ein gutes Domänenmodell ist nicht nur verständlicher, sondern auch schwerer anzugreifen.

Grundbegriffe aus DDD

Das dritte Kapitel führt in die Teile von Domain-Driven Design ein, die das Buch braucht. Wer Evans kennt, findet nichts Neues, wohl aber den Sicherheitsblick auf jeden Begriff:

BegriffBedeutungBezug zur Sicherheit
Domänenmodellein bewusst vereinfachtes, präzises Abbild der FachlichkeitJe genauer das Modell, desto weniger ungültige Zustände sind darstellbar.
Wertobjektdefiniert durch seine Werte, unveränderlichUnveränderliche Werte können nach der Prüfung nicht mehr verfälscht werden.
Entitäthat eine Identität und einen veränderlichen ZustandZustandsänderungen müssen kontrolliert und regelgerecht ablaufen.
Aggregateine Gruppe von Objekten mit gemeinsamen Regeln und einem EinstiegspunktRegeln, die mehrere Objekte betreffen, werden an einer Stelle durchgesetzt.
Bounded Contextein Bereich, in dem ein Begriff eine eindeutige Bedeutung hatAn Grenzen zwischen Kontexten muss übersetzt und geprüft werden; dort entstehen viele Lücken.

Kontextgrenzen als Sicherheitsgrenzen

Die Autoren betonen besonders die Grenzen zwischen Bounded Contexts. Dasselbe Wort kann auf beiden Seiten etwas anderes bedeuten, etwa „Kunde“ im Vertrieb und in der Buchhaltung. Wo Daten eine Grenze überqueren, ohne übersetzt und geprüft zu werden, entstehen Missverständnisse, und Missverständnisse sind Einfallstore. Das spätere Zwischenkapitel über eine Versicherung, die versehentlich Policen ohne Zahlung ausstellte, weil zwei Systeme „bezahlt“ unterschiedlich verstanden, illustriert das.

Einordnung

Der Abschnitt ist eine kompakte Wiederholung von Evans und eine Überleitung zu Vernon, der dieselben Begriffe in der Umsetzung vertieft. Die Antikorruptionsschicht aus Evans‘ strategischem Design bekommt hier eine zusätzliche Rolle: Sie übersetzt nicht nur Modelle, sie ist auch eine Prüfstelle.

Sichere Code-Konstrukte

Das vierte Kapitel beschreibt drei Gewohnheiten, die auf Code-Ebene Sicherheit fördern.

Unveränderlichkeit

Unveränderliche Objekte können nach ihrer Erzeugung nicht mehr in einen ungültigen oder manipulierten Zustand geraten. Sie sind außerdem ohne Synchronisation zwischen Threads teilbar, was eine ganze Klasse von Fehlern bei Nebenläufigkeit verhindert, wie Goetz u.a. beschreiben. Das schützt Integrität und, weil weniger Sperren nötig sind, auch Verfügbarkeit.

Früh scheitern mit Verträgen

Jede Methode und jeder Konstruktor prüft seine Vorbedingungen sofort und bricht bei Verletzung ab. Ein Objekt, das mit ungültigen Daten nicht erzeugt werden kann, kann später keinen Schaden anrichten. Die Autoren empfehlen, Verträge explizit im Code zu formulieren, etwa mit Objects.requireNonNull und eigenen Prüfmethoden, statt sich auf Kommentare oder Annahmen zu verlassen.

Prüfen in der richtigen Reihenfolge

Besonders nützlich ist die empfohlene Reihenfolge für die Prüfung von Eingaben. Sie beginnt mit billigen Prüfungen, die keine Analyse brauchen, und endet mit teuren, die Fachwissen und oft Datenbankzugriffe erfordern.

die empfohlene Reihenfolge für die Prüfung von Eingaben

die empfohlene Reihenfolge für die Prüfung von Eingaben

SchrittBeispielWarum an dieser Stelle
HerkunftStammt die Anfrage von einem bekannten System?schützt vor Angriffen, bevor überhaupt Daten gelesen werden
GrößeIst die Eingabe höchstens 10 KB lang?verhindert, dass riesige Eingaben Speicher und Rechenzeit verbrauchen
ZeichenEnthält sie nur erlaubte Zeichen?schließt Steuerzeichen und viele Einschleusungen aus
SyntaxHat sie das richtige Format, etwa eine IBAN?erst jetzt lohnt sich das Parsen
SemantikExistiert das Konto, und darf dieser Kunde darauf zugreifen?braucht Fachregeln und Daten, deshalb zuletzt

Die Reihenfolge schützt die teuren Prüfungen: Ein Angreifer kann das System nicht überlasten, indem er riesige oder sinnlose Eingaben schickt, die erst nach aufwendiger Verarbeitung abgelehnt würden.

Einordnung

Unveränderlichkeit und frühes Scheitern empfiehlt Bloch in Effective Java aus Gründen der Korrektheit; hier zeigt sich, dass sie auch Sicherheitsmechanismen sind. Die Prüfreihenfolge passt zu Nygards Stabilitätsmustern: Wer billig vorprüft, schützt das System vor Last, die es nie hätte annehmen sollen. In Java-Records lässt sich das kompakt umsetzen, wie das nächste Kapitel zeigt.

Domänenprimitive

Das fünfte Kapitel ist das Herz des Buchs.

Was ein Domänenprimitiv ist

Ein Domänenprimitiv ist ein Wertobjekt mit zwei Eigenschaften: Es ist unveränderlich, und es kann nur gültige Werte annehmen; die Prüfung geschieht bei der Erzeugung. Es ersetzt die primitiven Typen der Sprache, also int, String oder BigDecimal, überall dort, wo ein Wert eine fachliche Bedeutung hat: Menge, Kontonummer, Postleitzahl, Versicherungsnummer.

// Domänenprimitiv: eine Bestellmenge, die nur gültige Werte kennt
public record Quantity(int value) {
    private static final int MAX = 500;

    public Quantity {
        if (value < 1 || value > MAX)
            throw new IllegalArgumentException("Menge muss zwischen 1 und " + MAX + " liegen");
    }

    public Quantity add(Quantity other) { return new Quantity(value + other.value); }   // bleibt gültig oder scheitert
}

// Prüfung in der empfohlenen Reihenfolge: Größe, Zeichen, Syntax
public record PolicyNumber(String value) {
    private static final Pattern FORMAT = Pattern.compile("[A-Z]{2}-\\d{8}");

    public PolicyNumber {
        Objects.requireNonNull(value);
        if (value.length() != 11)                       throw new IllegalArgumentException("Länge");
        if (!value.chars().allMatch(c -> c < 128))      throw new IllegalArgumentException("Zeichen");
        if (!FORMAT.matcher(value).matches())           throw new IllegalArgumentException("Format");
    }
}
// Die Semantik, etwa „existiert diese Police und darf der Nutzer sie sehen?“,
// gehört in den Anwendungsdienst, nicht in den Wert.

Auf Primitiven stehen

Die Autoren empfehlen, Domänenprimitive überall zu verwenden, besonders in Methodensignaturen. Eine Methode transfer(Iban from, Iban to, Amount amount) kann nicht mit vertauschten oder ungültigen Werten aufgerufen werden; transfer(String, String, BigDecimal) schon. Die Prüfung findet einmal an der Grenze statt, danach kann der Rest des Codes den Werten vertrauen.

Werte, die man nur einmal lesen darf

Für sensible Daten wie Passwörter oder Kartennummern schlagen die Autoren Einmal-Lese-Objekte vor:

// Ein Geheimnis, das man genau einmal lesen kann und das nicht versehentlich ausgegeben wird
public final class Password {
    private char[] value;
    private final AtomicBoolean consumed = new AtomicBoolean(false);

    public Password(char[] value) { this.value = value.clone(); }

    public char[] value() {
        if (consumed.getAndSet(true)) throw new IllegalStateException("Bereits gelesen");
        char[] copy = value.clone();
        Arrays.fill(value, '0');                                   // Original überschreiben
        return copy;
    }

    @Override public String toString() { return "Password[***]"; }  // nie im Log
}

So landet ein Passwort nicht versehentlich im Log, in einer Fehlermeldung oder in einem zweiten Codepfad, der es nicht sehen sollte.

Einordnung

Domänenprimitive sind Evans‘ Wertobjekte mit einer zusätzlichen, strengen Regel. In Fowlers Refactoring heißt das Gegenteil „Primitive Obsession“ und gilt als Code-Smell, samt Refactorings, um es zu beheben. Mit Java-Records sind Domänenprimitive heute so knapp zu schreiben, dass kaum ein Grund bleibt, darauf zu verzichten; Weidig zeigt in A Functional Approach to Java denselben Stil.

Zustand: Integrität und Komplexität

Werte lassen sich unveränderlich machen, Entitäten nicht: Eine Versicherungspolice wird beantragt, angenommen, geändert, gekündigt. Zwei Kapitel behandeln, wie man veränderlichen Zustand sicher hält.

Integrität sichern

RegelBegründung
Vollständige KonstruktorenEin Objekt entsteht mit allen Pflichtangaben in gültigem Zustand. Leere Konstruktoren mit nachträglichen Settern, wie bei JavaBeans üblich, erlauben halbfertige Objekte.
Erbauer mit PrüfungFür Objekte mit vielen Angaben sammelt ein Builder die Werte und prüft alles beim Erzeugen.
Keine nackten SetterZustandsänderungen geschehen über fachliche Methoden wie cancel(), die Regeln durchsetzen, nicht über setStatus().
Innereien nicht herausgebenInterne Listen werden nur als unveränderliche Kopie oder Sicht nach außen gegeben, sonst kann jeder sie ändern.

Komplexität verringern

Je mehr Zustände ein Objekt haben kann, desto mehr Kombinationen müssen korrekt behandelt werden, und desto mehr Lücken gibt es. Die Autoren beschreiben vier Muster, um das zu begrenzen:

MusterIdee
Teilweise unveränderliche EntitätenWas sich nie ändert, etwa die Kennung, ist unveränderlich.
ZustandsobjektDie erlaubten Zustände und Übergänge werden in ein eigenes Objekt ausgelagert und dort geprüft.
SchnappschussLesende Teile des Systems bekommen eine unveränderliche Momentaufnahme statt der Entität selbst.
StaffelübergabeDer Lebenszyklus wird in Phasen zerlegt, jede mit eigenem Typ; ein Antrag wird zur Police, eine Police zur gekündigten Police.

Die Staffelübergabe lässt sich in modernem Java besonders elegant ausdrücken:

// Jede Phase ist ein eigener Typ; ungültige Übergänge kompilieren nicht
public sealed interface PolicyLifecycle permits Application, ActivePolicy, CancelledPolicy {}

public record Application(ApplicationId id, Customer customer, Coverage coverage) implements PolicyLifecycle {
    public ActivePolicy accept(PolicyNumber number, LocalDate start) {
        return new ActivePolicy(number, customer, coverage, start);
    }
}

public record ActivePolicy(PolicyNumber number, Customer customer, Coverage coverage, LocalDate start)
        implements PolicyLifecycle {
    public CancelledPolicy cancel(LocalDate effective, CancellationReason reason) {
        if (effective.isBefore(start)) throw new IllegalArgumentException("Kündigung vor Beginn");
        return new CancelledPolicy(number, effective, reason);
    }
}

public record CancelledPolicy(PolicyNumber number, LocalDate effective, CancellationReason reason)
        implements PolicyLifecycle {}
// Eine gekündigte Police hat keine Methode cancel(); ein Antrag keine Methode cancel().

Einordnung

Die Staffelübergabe ist Weidigs funktionaler Stil mit Records und versiegelten Typen und erinnert an das Zustandsmuster der Gang of Four, nur dass die Zustände unveränderlich sind und der Compiler Übergänge prüft. Die Regel gegen nackte Setter ist Evans‘ Forderung nach einer ausdrucksstarken, fachlichen Schnittstelle der Entitäten; Vernon vertieft sie bei den Aggregaten.

Sicherheit in der Pipeline

Das achte Kapitel zeigt, wie man die Auslieferungspipeline für Sicherheit nutzt, ohne eigene Sicherheitswerkzeuge in den Vordergrund zu stellen.

Vier Arten von Testfällen

Für jedes Domänenprimitiv und jede Prüfung empfehlen die Autoren vier Kategorien:

KategorieBeispiel für Quantity
Normale Eingaben1, 5, 42
Grenzwerte0, 1, 500, 501
Ungültige Eingabennegative Zahlen, null
Extreme EingabenInteger.MAX_VALUE, sehr lange Zeichenketten, ungewöhnliche Zeichen

Die meisten Teams testen nur die erste Kategorie, Angreifer nutzen die letzten drei.

Weitere Prüfungen in der Pipeline

  • Feature-Schalter testen: Schalter sind Konfiguration mit Wirkung auf Verhalten; falsch gesetzte oder wiederverwendete Schalter haben schon zu schweren Vorfällen geführt. Jede relevante Kombination muss getestet sein.
  • Konfiguration prüfen: Sicherheitsrelevante Einstellungen wie TLS-Versionen, Header oder Standardpasswörter werden automatisch geprüft.
  • Verfügbarkeit testen: Lasttests und Tests für Überlastsituationen gehören zur Sicherheit, weil Verfügbarkeit ein Schutzziel ist.
  • Automatische Sicherheitsscans: Abhängigkeiten auf bekannte Schwachstellen prüfen, ergänzend zu den Design-Maßnahmen.

Fehler sicher behandeln

Das neunte Kapitel behandelt, wie Fehler Sicherheitslücken öffnen.

ProblemEmpfehlung
Ausnahmen verraten zu vielFehlermeldungen und Logs enthalten keine sensiblen Daten wie Kontonummern, Abfragen oder Stacktraces nach außen
Fachliche und technische Fehler vermischtfachliche Fehler, etwa „Konto gesperrt“, getrennt von technischen, etwa „Datenbank nicht erreichbar“, behandeln
Fehler als Ausnahme statt als Ergebniserwartbare fachliche Fehler als Rückgabewert modellieren, etwa mit einem Ergebnis-Typ, statt sie durch Ausnahmen zu signalisieren
Kaputte Daten reparierenungültige Daten ablehnen statt stillschweigend korrigieren; eine „Reparatur“ kann Angriffe verschleiern
Kaskadierende AusfälleCircuit Breaker, Bulkheads und Timeouts, wie bei Nygard

Einordnung

Die vier Testkategorien ähneln Huyens Störungs- und Invarianztests für ML-Modelle, auf klassischen Code übertragen, und Khorikovs Forderung, Verhalten statt Implementierung zu testen. Die Fehlerbehandlung verbindet Nygards Release It! mit Sicherheit: Ein System, das unter Last zusammenbricht, ist nicht nur instabil, sondern auch angreifbar. Fehler als Ergebnis zu modellieren, passt zu Weidigs Kapitel über funktionale Fehlerbehandlung.

Cloud-Denkweise

Das zehnte Kapitel zeigt, dass Prinzipien für Cloud-Anwendungen, etwa die bekannten „Twelve-Factor“-Regeln, auch der Sicherheit dienen:

PrinzipSicherheitsnutzen
Konfiguration aus der UmgebungGeheimnisse stehen nicht im Code oder im Repository
Logs als Datenstrom an einen zentralen Dienstkeine Logdateien mit sensiblen Daten auf einzelnen Servern; zentrale Kontrolle über Zugriff und Aufbewahrung
Zustandslose ProzesseServer lassen sich jederzeit austauschen

Daraus leiten die Autoren drei Gewohnheiten ab: Zugangsdaten regelmäßig erneuern, Server regelmäßig aus einem bekannten, sauberen Stand neu aufsetzen und Schwachstellen sofort beheben. Ein Angreifer, der sich eingenistet hat, verliert so seinen Halt, und gestohlene Zugangsdaten verfallen schnell.

Legacy-Code

Das zwölfte Kapitel ist besonders praxisnah. Es beschreibt Warnzeichen in bestehendem Code und wie man sie schrittweise beseitigt:

WarnzeichenVorgehen
Mehrdeutige Parameterlisten, etwa mehrere String-Parameter hintereinanderschrittweise durch Domänenprimitive ersetzen, von außen nach innen
Ungeprüfte Zeichenketten im Lognur geprüfte Werte loggen; sonst drohen Einschleusung in Logs und Datenlecks
Defensive Prüfungen überall, etwa null-Abfragen in jeder MethodeZeichen fehlender Domänenprimitive; die Prüfung gehört an die Grenze
Falsch verstandenes DRYGleicher Text ist nicht gleiches Konzept; zusammengelegter Code für verschiedene Konzepte koppelt, was getrennt sein sollte
Halbe Domänenprimitive, etwa ein Betrag ohne Währungvervollständigen, damit der Typ das ganze Konzept trägt
Tests nur für den Normalfalldie vier Testkategorien ergänzen

Microservices

Das dreizehnte Kapitel überträgt die Ideen auf verteilte Systeme. Jeder Dienst sollte einem Bounded Context entsprechen, seine Schnittstelle ist eine öffentliche Grenze und muss Eingaben vollständig prüfen, auch wenn sie von einem „vertrauten“ internen Dienst kommen. Sensible Daten werden klassifiziert und nur dorthin geschickt, wo sie gebraucht werden. Beim Logging über viele Dienste hinweg braucht man Nachverfolgbarkeit, ohne sensible Daten zu verbreiten.

Grenzen des Ansatzes

Das letzte Kapitel betont, dass Design nicht alles löst. Bedrohungsanalysen, Penetrationstests, Wissen über typische Angriffe, Notfallprozesse und Sicherheitsfachleute bleiben nötig. Design reduziert die Zahl der Lücken, die diese Maßnahmen finden müssen.

Einordnung

Das Kapitel über Legacy-Code ist Feathers‘ Werkzeugkasten aus Sicherheitssicht; das Ersetzen mehrdeutiger Parameterlisten ist ein Refactoring im Sinne Fowlers. Die Warnung vor falsch verstandenem DRY deckt sich mit Hunt und Thomas, die DRY ausdrücklich auf Wissen beziehen, nicht auf Text. Das Microservice-Kapitel ergänzt Newman: Jede Dienstgrenze ist auch eine Vertrauensgrenze.

Kritik und Einordnung

Stärken

  • Der richtige Hebel: Sicherheit als Ergebnis von Design-Gewohnheiten, die Entwickler ohnehin pflegen, statt als zusätzliche Last.
  • Domänenprimitive: ein einfaches, sofort anwendbares Konzept mit großer Wirkung.
  • Java durchgehend: Die Beispiele lassen sich direkt übernehmen und mit Records noch knapper schreiben.
  • Legacy-Kapitel: konkrete Warnzeichen und schrittweise Verbesserungen statt Neubau.
  • Brücke zwischen Welten: verbindet DDD, Clean Code, Continuous Delivery und Stabilitätsmuster mit Sicherheit.

Kritikpunkte

1. Kein umfassendes Sicherheitsbuch. Authentifizierung, Autorisierung, Kryptografie, Bedrohungsmodellierung und typische Angriffe auf Webanwendungen werden kaum behandelt. Das sagen die Autoren selbst; Leser sollten es nicht als einziges Sicherheitsbuch verstehen.

2. Wiederholungen. Manche Ideen, etwa Unveränderlichkeit und Prüfung bei der Erzeugung, kehren in mehreren Kapiteln wieder.

3. Stand von 2019. Records, versiegelte Typen und moderne Cloud-Werkzeuge zur Geheimnisverwaltung kommen nicht vor; die Prinzipien gelten trotzdem, die Umsetzung ist heute einfacher.

4. Aufwand in großen Codebasen. Domänenprimitive konsequent einzuführen, erfordert viele kleine Typen und Umstellungen. Die Autoren zeigen den schrittweisen Weg, unterschätzen aber, wie viel Überzeugungsarbeit im Team nötig ist.

5. Neue Angriffsflächen fehlen. Anwendungen mit Sprachmodellen bringen Risiken wie Prompt-Injection, die sich nicht allein durch Domänenmodellierung abwehren lassen; dafür ist Huyen die Ergänzung.

Gesamturteil

Das beste Buch, um Sicherheit in den Entwickleralltag zu bringen, ohne aus Entwicklern Sicherheitsfachleute machen zu müssen. Für Java-Teams, die DDD schon nutzen oder einführen wollen, ist es besonders wertvoll. Als Ergänzung empfiehlt sich ein klassisches Werk zu Webanwendungssicherheit und Bedrohungsmodellierung.

Bezüge zur Reihe

Thema in Secure by DesignBezug in der Reihe
Tiefes Modell statt Zahl, negative MengeEvans: tiefe Modellierung; Ousterhout: Sonderfälle wegdefinieren
DomänenprimitiveEvans: Wertobjekte; Fowler: Primitive Obsession; Bloch: unveränderliche Klassen
Records und versiegelte TypenWeidig: funktionaler Stil in Java; Bloch: Records
UnveränderlichkeitGoetz u.a.: sichere Nebenläufigkeit; Bloch: Unveränderlichkeit bevorzugen
Früh scheitern, VorbedingungenHunt und Thomas: Design by Contract, früh abstürzen
Prüfreihenfolge, billig zuerstNygard: Last abweisen, bevor sie schadet
Vollständige Konstruktoren, keine SetterBloch: Builder; Martin: Kapselung
Staffelübergabe der ZuständeGang of Four: Zustandsmuster; Vernon: Aggregate
Kontextgrenzen als PrüfstellenEvans: Antikorruptionsschicht; Newman: Dienstgrenzen
Vier Kategorien von TestfällenKhorikov: Verhalten testen; Huyen: Störungs- und Invarianztests
Feature-Schalter und Konfiguration testenHumble und Farley: Konfiguration ist Code
Fehler sicher behandeln, ResilienzNygard: Stabilitätsmuster; Weidig: funktionale Fehlerbehandlung
Erneuern, neu aufsetzen, reparierenSRE-Buch: Automatisierung; Humble und Farley: unveränderliche Infrastruktur
Warnzeichen in Legacy-CodeFeathers: Legacy-Code; Fowler: Refactoring
DRY bezieht sich auf WissenHunt und Thomas: DRY
Grenzen des AnsatzesHuyen: Prompt-Injection; Goodfellow u.a.: adversarielle Beispiele

Diskussionsfragen

  1. Negative Mengen: Wo in euren Systemen könnte ein „Wert, der keine Zahl ist“ ungültige Zustände zulassen?
  2. Domänenprimitive: Wie viele fachliche Werte werden bei euch als String oder BigDecimal durchgereicht? Wo würdet ihr anfangen?
  3. Prüfreihenfolge: Prüfen eure Schnittstellen Größe und Zeichen, bevor sie parsen?
  4. Setter: Wie viele eurer Entitäten haben öffentliche Setter? Was würde passieren, wenn ihr sie durch fachliche Methoden ersetzt?
  5. Fehlermeldungen: Welche sensiblen Daten landen bei euch in Logs oder Fehlermeldungen?
  6. Tests: Enthalten eure Tests Grenzwerte und extreme Eingaben, oder nur den Normalfall?
  7. Dienstgrenzen: Vertrauen eure internen Dienste einander blind?
  8. Ergänzung: Welche Sicherheitsthemen jenseits des Designs bräuchte euer Team am dringendsten?