4.4 KiB
NEXARCH Core – Projektweite Sicherheits-Coding-Konventionen
Stand: 2026-08-28. Ticket: IAM-15. Ergänzt docs/TESTSTRATEGIE-CORE.md (QA-01) um Coding-Regeln,
die als Code-Review-Checkliste gelten — keine dieser Regeln ist optional oder "nur für ein Modul".
1. Warum dieses Dokument existiert
Eine sicherheitsrelevante Coding-Regel, die nur einmal an einer Stelle vorgemacht statt projektweit verankert wird, wird beim nächsten neuen Vergleich vergessen. Das gilt für jede Regel in diesem Dokument gleichermaßen — die erste Regel (SQL) ist bereits als Konvention etabliert, die zweite (timing-safe Vergleich, IAM-15) macht sie hier zum ersten Mal explizit schriftlich.
2. Regel: Kein fmt.Sprintf für SQL-Bestandteile aus Nutzereingabe
Spalten-/Tabellennamen ausschließlich aus statischen Konstanten bzw. einem geschlossenen
Enum/Switch-Typ, nie aus Nutzereingabe oder generischem String-Zusammenbau — auch nicht hinter
einer Whitelist-Funktion. Werte (nicht Bezeichner) gehören als Parameter ($1, $2, …) in die
Query, niemals interpoliert.
Lehre aus beiden Altsystemen (known-issues-archivdms.md Punkt 10, known-issues-archivmail.md
Punkt 12): dynamische Tabellennamen via fmt.Sprintf, nur durch eine fragile Whitelist-Funktion
abgesichert. Siehe DMS/Mail SRC-11 für die board-spezifische Umsetzung dieser Regel im
Suchindex-Kontext.
Referenzbeispiel (korrekt): internal/tenant/lifecycle.go, ProcessDueDeletions — Statuswerte
und IDs ausschließlich als Parameter ($1, $2, …), niemals interpoliert; der einzige Einsatz von
fmt.Sprintf im Package baut einen Datenbanknamen aus einem bereits validierten Slug
(dbNameForSlug, slugPattern in tenant.go erzwingt ^[a-z][a-z0-9_]{1,48}$ vor jeder
Verwendung) — keine ungeprüfte Nutzereingabe erreicht die Query.
3. Regel: Timing-safe Vergleich für jede sicherheitsrelevante Zugriffsentscheidung (IAM-15)
Jeder Vergleich, der eine sicherheitsrelevante Zugriffsentscheidung trifft — Passwort-Hash, Token,
Signatur, 2FA-Code/-Wiederherstellungscode — nutzt einen timing-safe/constant-time Vergleich, nie
den regulären ==-Operator. Ein naiver ==-Vergleich zweier Byte-Folgen bricht bei der ersten
abweichenden Stelle ab; die dadurch messbare Laufzeitdifferenz lässt sich aus der Ferne ausmessen und
erlaubt ein Byte-für-Byte-Erraten des korrekten Werts (Timing-Angriff).
So wird es gemacht: internal/timingsafe (dieses Ticket) bündelt die kanonische Implementierung
(crypto/subtle.ConstantTimeCompare) für neue Vergleichsstellen:
import "gitea.perlbach24.de/scripte/nexarch/internal/timingsafe"
if !timingsafe.EqualString(providedCode, expectedCode) {
return ErrInvalid
}
Ausnahme: bcrypt.CompareHashAndPassword (Passwort-Hashes) ist bereits von Haus aus timing-safe —
hier ist kein zusätzlicher Wrapper nötig.
3.1 Audit bestehender Vergleichsstellen (Prüfung 2)
Durchgeführt 2026-08-28, Ergebnis: alle bestehenden sicherheitsrelevanten Vergleichsstellen
implementierten die Regel bereits korrekt, unabhängig voneinander mit crypto/subtle — nichts
musste korrigiert werden (Akzeptanzkriterium 3, „ggf.").
| Ort | Was wird verglichen | Fundstelle |
|---|---|---|
internal/totp/totp.go, Validate |
TOTP-Code (2FA) | nutzte bereits subtle.ConstantTimeCompare direkt, in diesem Ticket auf timingsafe.EqualString umgestellt (erster Verwender des neuen Packages) |
internal/webhook/dispatcher.go, VerifySignature |
HMAC-Webhook-Signatur | subtle.ConstantTimeCompare(expectedBytes, gotBytes) |
internal/moduleregistry/credentials.go, Authenticate |
Service-Credential-Secret-Hash | eigene timingSafeEqual-Hilfsfunktion, gleiches Muster |
internal/authtoken/token.go, Consume (Passwort-Reset/Einladung) |
Einmal-Token | Hash-Lookup über DB-Index (WHERE token_hash = $1), kein manueller Byte-Vergleich nötig — bei zufälligen, hochentropischen Token ist der indexierte Hash-Abgleich gleichwertig sicher |
Neue Vergleichsstellen sollen internal/timingsafe verwenden, statt das Muster erneut inline zu
duplizieren — bestehende Stellen müssen dafür nicht umgebaut werden (kein Umbau angrenzender
Bereiche über Board-Branch-Grenzen hinweg).
4. Wie diese Liste wächst
Neue projektweite Sicherheits-Coding-Regeln werden hier ergänzt, sobald sie (wie SQL-Sprintf und timing-safe Vergleich) mehr als einmal unabhängig als Lehre auftauchen — nicht vorab spekulativ.