Secure by Design
Entwicklungsphilosophie, bei der Sicherheit von Anfang an in Architektur und Code eingebettet wird - nicht als nachträglicher Patch. Secure-by-Design-Prinzipien umfassen minimale Angriffsfläche, sichere Defaults, Defense-in-Depth und fail-safe Defaults.
Secure by Design ist mehr als ein Buzzword - es ist eine Ablehnung der “Security Retrofit”-Mentalität. Wenn ein Produkt erst entwickelt, dann auf Sicherheit “geprüft” und schließlich mit Patches repariert wird, entstehen strukturelle Schwachstellen die sich nicht flicken lassen. Secure by Design dreht diese Reihenfolge um: Bedrohungsmodellierung vor der ersten Codezeile.
Die CISA Secure by Design Prinzipien (2023)
Die US-Behörde CISA hat 2023 gemeinsam mit internationalen Sicherheitsbehörden (darunter das deutsche BSI) Prinzipien für Secure by Design veröffentlicht:
Prinzip 1: Hersteller übernehmen Verantwortung für Sicherheit
- Nicht: “Nutzer müssen sicher konfigurieren”
- Sondern: Sichere Defaults aus der Box
- Beispiel: Passwörter nicht standardmäßig “admin/admin”, sondern zufällig generiert oder mit Änderungszwang
Prinzip 2: Radikale Transparenz und Verantwortlichkeit
- CVE-Veröffentlichung ohne Verzögerung
- Klare EOL-Dates (End of Life)
- Security-Changelogs in Release Notes
Prinzip 3: Kunden-Sicherheit als Kernziel
- Sicherheit ist kein Premium-Feature
- Nicht: “MFA ist verfügbar in der Enterprise-Variante”
- Sondern: MFA ist Standard und kostenlos
Die sechs Secure-by-Design-Prinzipien
1. Minimale Angriffsfläche (Attack Surface Reduction)
Jede unnötige Funktion ist eine potenzielle Schwachstelle.
- Nur installieren was gebraucht wird
- Ports/Services: nur das Minimum öffnen
- Falsch: 12 Services beim Start, Nutzer konfiguriert ab
- Richtig: 3 Core-Services, Nutzer aktiviert optional
# Alle Ports die standardmäßig gebunden werden:
ss -tlnp # Jede Zeile hinterfragen: Braucht man das?
2. Sichere Defaults
Aus der Box sicher, nicht “kann sicher konfiguriert werden”.
<!-- FALSCH: -->
<encryption enabled="false"> <!-- Default in config.xml -->
RICHTIG: Verschlüsselung IMMER, kein Opt-out für Daten in Ruhe
FALSCH: Admin-UI default auf 0.0.0.0 (alle Interfaces)
RICHTIG: Admin-UI default auf 127.0.0.1 (nur lokal)
3. Defense in Depth
Keine einzelne Schutzschicht ist ausreichend. Bei Schicht 1 Versagen hält Schicht 2 noch.
Beispiel Web-App:
| Schicht | Maßnahme | Schützt gegen |
|---|---|---|
| Layer 1 | WAF | Blockt bekannte Angriffe |
| Layer 2 | Input Validation | Blockt Injection |
| Layer 3 | Prepared Statements | Blockt SQLi auch wenn Layer 2 versagt |
| Layer 4 | Minimale DB-Rechte | Begrenzt Schaden wenn Layer 3 versagt |
| Layer 5 | Encryption at rest | Begrenzt Schaden wenn Layer 4 versagt |
4. Fail Safe (sicheres Verhalten bei Fehlern)
Bei Fehler: standardmäßig blockieren, nicht erlauben.
// FALSCH:
try { checkPermission(user, resource); }
catch (Exception e) {
log.error("Permission check failed");
return true; // Zugang gewähren wenn Fehler!
}
// RICHTIG:
try { return checkPermission(user, resource); }
catch (Exception e) {
log.error("Permission check failed - denying access");
return false; // Bei Fehler: KEIN Zugang
}
5. Least Privilege
Jede Komponente/Prozess/User erhält nur die minimalen Rechte:
- DB-User hat nur SELECT auf nötige Tabellen
- Container läuft nicht als root
- API-Key hat nur read-Scope wenn write nicht nötig ist
6. Psychologische Akzeptanz (Usability)
Sichere Optionen müssen die einfachste Option sein. Wenn Sicherheit mühsam ist, umgeht der User sie. “Security Theater” (sieht sicher aus, ist es nicht) vermeiden.
Threat Modeling - Sicherheit vor dem Code
Wann: Vor der Implementierung, in der Design-Phase Wer: Entwickler + Security-Experte + Architect Output: Liste von Bedrohungen → Mitigationen → Anforderungen
STRIDE-Methode (Microsoft)
| Buchstabe | Kategorie | Beschreibung |
|---|---|---|
| S | Spoofing | Angreifer gibt sich als jemand anderes aus |
| T | Tampering | Angreifer manipuliert Daten |
| R | Repudiation | Angreifer leugnet Aktionen |
| I | Information Disclosure | Datenleck |
| D | Denial of Service | System lahmlegen |
| E | Elevation of Privilege | Rechte erlangen |
STRIDE für eine einfache REST-API
Endpunkt: POST /api/orders (Bestellung aufgeben)
| Kategorie | Bedrohung | Mitigation |
|---|---|---|
| Spoofing | Angreifer bestellt als anderer User | Starke Auth (JWT + audience-Check), MFA für hohe Beträge |
| Tampering | Angreifer ändert Bestellpreis in Request | Preise immer serverseitig berechnen, nie vom Client |
| Repudiation | ”Ich habe das nie bestellt” - keine Beweise | Audit-Log mit User-ID, Timestamp, IP, Request-Hash |
| Info Disclosure | Error-Message zeigt DB-Schema | Generische Fehlermeldungen im Prod, Details nur in Logs |
| DoS | 10.000 Bestellungen/Sekunde → DB überlastet | Rate Limiting (100/min pro User), Queue für Processing |
| EoP | Angreifer fügt admin=true in POST-Body ein | Whitelist-Input-Validation, DTO-Mapping ohne admin-Feld |
Secure-by-Design in der Praxis: Code-Beispiele
// ===== UNSICHER: Klassische Fehler =====
// 1. SQL Injection durch String-Konkatenation
const query = `SELECT * FROM users WHERE email = '${userInput}'`;
// Payload: ' OR '1'='1 → alle User zurückgegeben
// 2. Fehlermeldung leckt Systeminfos
app.use((err, req, res, next) => {
res.json({ error: err.stack }); // Stack Trace inkl. Dateipfade!
});
// 3. IDOR: User greift auf fremde Ressource zu
app.get('/invoice/:id', (req, res) => {
const invoice = db.query('SELECT * FROM invoices WHERE id = ?', req.params.id);
res.json(invoice); // Keine Prüfung: gehört Invoice dem User?
});
// ===== SICHER: Secure-by-Design =====
// 1. Prepared Statements (SQLi-sicher)
const query = 'SELECT * FROM users WHERE email = ?';
const user = await db.query(query, [userInput]);
// 2. Fehlerbehandlung ohne Info-Leak
app.use((err, req, res, next) => {
const errorId = crypto.randomUUID();
logger.error({ errorId, stack: err.stack, path: req.path });
res.status(500).json({
error: 'An internal error occurred',
errorId // Zum Nachschlagen im Log, ohne Details
});
});
// 3. Ownership-Check (IDOR-sicher)
app.get('/invoice/:id', requireAuth, async (req, res) => {
const invoice = await db.query(
'SELECT * FROM invoices WHERE id = ? AND user_id = ?',
[req.params.id, req.user.id] // IMMER user_id mit prüfen!
);
if (!invoice) return res.status(404).json({ error: 'Not found' });
res.json(invoice);
});
// 4. Input Validation (Zod-Schema)
import { z } from 'zod';
const OrderSchema = z.object({
productId: z.string().uuid(),
quantity: z.number().int().min(1).max(100),
// Kein 'price' oder 'admin' Feld - wird ignoriert wenn gesendet
});
app.post('/orders', requireAuth, async (req, res) => {
const result = OrderSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ error: result.error.issues });
}
const { productId, quantity } = result.data; // Nur validierte Daten!
// Preis serverseitig aus DB holen:
const product = await db.getProduct(productId);
const totalPrice = product.price * quantity;
// ...
});
Secure by Default - Checkliste für neue Projekte
Authentifizierung
- Passwörter: bcrypt/Argon2id (kein MD5, SHA-1, unsalted!)
- Session-Token: kryptographisch zufällig (
crypto.randomUUID) - JWT: kurze Gültigkeitsdauer (Access: 15min, Refresh: 7 Tage)
- MFA: optional anbieten, für Admins erzwingen
- Passwort-Reset: zeitbegrenzte, einmalige Token
Autorisierung
- RBAC oder ABAC: explizite Zugriffsrechte
- Ownership-Check: gehören Ressourcen dem anfragenden User?
- Deny-by-default: kein Zugang außer explizit erlaubt
- Admin-Funktionen: separater Auth-Check
Input/Output
- Input Validation: Schema-basiert (Zod, Joi, Pydantic)
- Output Encoding: XSS-Schutz (React: automatisch; sonst escapen)
- SQL: Prepared Statements oder ORM
- Fehlermeldungen: generisch für User, detailliert in Logs
Konfiguration
- Secrets: Env-Variablen (kein Hardcoding, kein Git!)
- HTTPS: erzwingen (HSTS)
- Security Headers: CSP, X-Frame-Options, X-Content-Type
- CORS: nur erlaubte Origins
- Debug-Mode: in Produktion deaktiviert
Logging & Monitoring
- Auth-Ereignisse loggen (Login OK/Fail, Passwort-Reset)
- Zugriffskontrolle loggen (Denied-Events)
- Keine sensiblen Daten in Logs (Passwörter, Tokens)
- Log-Level in Prod: INFO/WARN/ERROR (kein DEBUG)
CISA Secure by Design Pledge (2024)
CISA hat Softwarehersteller aufgefordert, innerhalb eines Jahres freiwillig folgende Ziele zu erfüllen:
- MFA-Anteil in eigenen Produkten messbar erhöhen
- Default-Passwörter in allen Produkten eliminieren
- Anzahl kritischer CVEs im eigenen Produkt-Portfolio reduzieren
- Transparenz über Schwachstellen-Offenlegungsprozess herstellen
- CVSSv3 Scores für alle Schwachstellen veröffentlichen
Stand 2025:
- 250+ Unternehmen haben unterzeichnet
- Darunter: Microsoft, Google, AWS, Cisco, Palo Alto Networks
- BSI befürwortet den Ansatz für deutsche Unternehmen
Bedeutung für Einkauf/Beschaffung:
- Produkte von Secure-by-Design-Pledgers bevorzugen
- Vertragsklauseln: Hersteller haftet für bekannte Schwachstellen
- EOL-Garantien vertraglich absichern