Voller Merge von RBAC-02/IAM-06/IAM-07/API-03/API-10/API-08/API-09/IAM-10/ IAM-11/IAM-13 plus echte Angriffstests (internal/pentest) gegen SSO/OIDC (alg=none, Fremdschluessel, Claims-Manipulation, Nonce-Replay), Rate- Limiting/Lockout im simulierten Mehrinstanz-Betrieb, zentrale Policy- Durchsetzung (Rechteausweitung, struktureller Guard-Bypass) und Master- Key-/Tenant-KEK-Rotation. 29/29 Pakete gruen auf 192.168.1.131. Vier real gefundene Testinfrastruktur-Fehler behoben: reset-test-env.sh liess tenant_keks (und weitere neuere Registry-Tabellen) beim Reset stehen (FK-CASCADE loescht nur die Constraint, keine Zeilen); zwei E2E-Tests und kek_test.go schlossen ihren adminPool per defer VOR ihrer t.Cleanup- Bereinigung (t.Cleanup laeuft immer nach allen defers); migrate_test.go hatte ein Testschema ohne die TEN-04-Lifecycle-Spalten. Alle vier Fixes betreffen ausschliesslich Testcode, kein Produktionscode geaendert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
150 lines
8.9 KiB
Markdown
150 lines
8.9 KiB
Markdown
# QA-04 – Prüfprotokoll: Sicherheits- & Penetrationsprüfung Core
|
||
|
||
Welle 6. Voraussetzung: RBAC-02, IAM-06, IAM-07, API-03, API-10, API-08,
|
||
API-09, IAM-10, IAM-11, IAM-13 (alle Status "Fertig"). Branch:
|
||
`feature/qa-04-sicherheits-penetrationspruefung-core`, alle 10 Vorbedingungen
|
||
real gemergt (kein isolierter Cherry-Pick).
|
||
|
||
## 1. Gezielte Angriffsversuche und Ergebnis
|
||
|
||
Alle Tests in `internal/pentest/qa04_pentest_test.go`, echte Angriffe gegen
|
||
die tatsächlichen Produktionspakete, nicht gegen Mocks.
|
||
|
||
### 1.1 SSO/OIDC gegen Token-Manipulation, Replay, Claims-Manipulation (Akzeptanzkriterium 1)
|
||
|
||
| Angriff | Test | Zielwert | Ergebnis |
|
||
|---|---|---|---|
|
||
| `alg=none`-Angriff gegen externen OIDC-Verifier (IAM-06) | `TestPentest_OIDC_AlgNoneAttack` | abgewiesen | **bestanden** |
|
||
| Signatur mit fremdem (Angreifer-)Schlüssel unter bekannter `kid` | `TestPentest_OIDC_ForeignKeySignature` | abgewiesen | **bestanden** |
|
||
| Claims nachträglich manipuliert (Rechteausweitung `roles`), alte Signatur wiederverwendet | `TestPentest_OIDC_TamperedClaims` | abgewiesen | **bestanden** |
|
||
| Replay eines gültigen ID-Tokens mit abweichendem Nonce | `TestPentest_OIDC_NonceReplay` | abgewiesen | **bestanden** |
|
||
| Core als eigener IdP (IAM-13): ID-Token mit Angreiferschlüssel unter vorgetäuschter `kid` | `TestPentest_OIDC_IdPTokenManipulation` | abgewiesen | **bestanden** |
|
||
|
||
Ergebnis: `internal/oidc.Verifier` erzwingt Algorithmus (`RS256` bzw.
|
||
`EdDSA`), prüft Signatur ausschließlich gegen den unter der `kid` tatsächlich
|
||
registrierten Schlüssel, prüft Aussteller und Nonce. Kein Umgehungsweg
|
||
gefunden.
|
||
|
||
### 1.2 Rate-Limiting/Account-Lockout im Mehrinstanz-Betrieb (Akzeptanzkriterium 2, Prüfung 1)
|
||
|
||
| Angriff | Test | Zielwert | Ergebnis |
|
||
|---|---|---|---|
|
||
| Rate-Limit über 4 unabhängige "Instanzen" (eigene DB-Pools) parallel umgehen | `TestPentest_RateLimit_MultiInstanceBypassAttempt` | Gesamtsumme erlaubter Anfragen = konfiguriertes Limit, nicht mehr | **bestanden** – 200 Anfragen über 4 Instanzen, exakt 100 (=Limit) erlaubt |
|
||
| Account-Lockout über 3 unabhängige Instanzen verteilte Brute-Force-Versuche umgehen | `TestPentest_Lockout_MultiInstanceBypassAttempt` | Konto wird trotz Verteilung gesperrt | **bestanden** – 12 Versuche über 3 Instanzen, Sperre nach Erreichen von `maxFailed=5` |
|
||
|
||
Ergebnis: beide Mechanismen nutzen ein atomares SQL-`UPSERT` gegen
|
||
geteilten, externen Zustand (Postgres) statt In-Process-Zähler – exakt der
|
||
in archivdms als Fehlerklasse dokumentierte Schwachpunkt wird hier vermieden.
|
||
|
||
### 1.3 Zentrale Policy-Durchsetzung, gezielter Umgehungsversuch je Modul (Akzeptanzkriterium 3, Prüfung 3)
|
||
|
||
| Angriff | Test | Zielwert | Ergebnis |
|
||
|---|---|---|---|
|
||
| Rechteausweitung: Rolle `user`/`tenant_admin` versucht `platform.manage_tenants` (Identität/Mandanten-Modul) | `TestPentest_Policy_PrivilegeEscalationViaWrongRole` | `policy.ErrDenied` | **bestanden** |
|
||
| Strukturelle Umgehung von `policy.Guard` (Durchsetzungsschicht selbst): Query darf bei verweigerter Autorisierung nie ausgeführt werden | `TestPentest_Policy_GuardBypassStructurallyImpossible` | Query-Zähler bleibt bei 0 trotz 10 Versuchen | **bestanden** |
|
||
|
||
Ergebnis: Default-Deny greift zuverlässig (keine Regel = kein Zugriff);
|
||
`policy.Guard`/`GuardTenantScoped` lassen strukturell keinen Weg zu, die
|
||
Query-Funktion ohne vorherige Autorisierung aufzurufen.
|
||
|
||
### 1.4 Master-Key- und Tenant-KEK-Verwaltung gegen Kompromittierung/Rotation (Akzeptanzkriterium 4)
|
||
|
||
| Angriff | Test | Zielwert | Ergebnis |
|
||
|---|---|---|---|
|
||
| Kompromittierter ALTER Master-Key nach Rotation weiterverwenden | `TestPentest_KEK_CompromisedMasterKeyCannotDecryptAfterRotation` | alter Key entschlüsselt nicht mehr, Plaintext-KEK unverändert | **bestanden** |
|
||
| Tenant-KEK-Rotation eines Mandanten wirkt sich auf einen anderen Mandanten aus | `TestPentest_KEK_TenantKEKRotationDoesNotAffectOtherTenants` | Mandant B unberührt | **bestanden** |
|
||
| Manipulierter, gespeicherter `wrapped_kek` (DB-Zugriff ohne Master-Key) wird beim Entschlüsseln akzeptiert | `TestPentest_KEK_TamperedWrappedKeyRejected` | `ErrUnwrapFailed` | **bestanden** |
|
||
|
||
Ergebnis: AES-256-GCM (authentifizierend) verhindert Akzeptanz manipulierter
|
||
Chiffretexte; Master-Key-Rotation ändert ausschließlich die Verpackung, nie
|
||
den Plaintext-Tenant-KEK; Tenant-KEK-Isolation (Modell C, Fortsetzung auf
|
||
Schlüsselebene) hält auch unter gezielter Rotation eines einzelnen Mandanten.
|
||
|
||
## 2. Beim Merge/Pentest gefundene Fehler (nicht vorab bekannt)
|
||
|
||
### 2.1 `scripts/reset-test-env.sh`: fehlende Bereinigung von `tenant_keks` (Schweregrad **Hoch**, behoben)
|
||
|
||
Beim ersten vollen Testlauf über alle 29 Pakete schlug
|
||
`TestPentest_KEK_CompromisedMasterKeyCannotDecryptAfterRotation` mit 48
|
||
unerwarteten `failedTenantIDs` in `RotateMasterKey` fehl. Ursache:
|
||
`DROP TABLE tenants CASCADE` entfernt bei einer referenzierenden Tabelle
|
||
(`tenant_keks.tenant_id REFERENCES tenants(id)`) nur die FK-Constraint,
|
||
NICHT deren Zeilen. Da `reset-test-env.sh` `tenant_keks` (API-10-Migration
|
||
`0006_tenant_keks.up.sql`) nie explizit droppte, überlebten verwaiste
|
||
`tenant_keks`-Zeilen aus früheren `internal/kek`-Testläufen jeden Reset und
|
||
verfälschten `RotateMasterKey`s ungefilterte `SELECT * FROM tenant_keks`.
|
||
|
||
Behoben: `reset-test-env.sh` droppt jetzt explizit alle Registry-Tabellen aus
|
||
`migrations/*.up.sql` (`tenant_keks`, `rate_limit_counters`,
|
||
`rate_limit_configs`, `policy_rule_changes`, `policy_rules`,
|
||
`feature_flags`, `module_credentials`, `modules`), nicht nur die vier
|
||
ursprünglich behandelten. Reiner Testinfrastruktur-Fix, keine
|
||
Produktionsmigration verändert.
|
||
|
||
### 2.2 `internal/e2e`-Tests: `defer adminPool.Close()` lief vor `t.Cleanup`-Bereinigung (Schweregrad **Mittel**, behoben)
|
||
|
||
Voller Testlauf zeigte anschließend `internal/migrate`-Fehlschläge
|
||
(„erwartet 3 Ergebnisse, habe 15" bzw. 12, mit Zeilen wie `e2e_tenant_a`).
|
||
Ursache: `TestE2E_TenantOnboardingLoginFlow` und `TestE2E_CrossTenantIsolation`
|
||
schlossen den `adminPool` per `defer adminPool.Close()`, ihre
|
||
`t.Cleanup`-Funktion (welche die angelegten Testmandanten wieder löscht)
|
||
lief aber – wie in Go üblich – ERST NACH allen `defer`-Aufrufen der
|
||
Testfunktion, also gegen einen bereits geschlossenen Pool. Der Fehler wurde
|
||
durch `_, _ = adminPool.Exec(...)` verschluckt; die Zeilen blieben dauerhaft
|
||
in der geteilten `tenants`-Tabelle stehen und verfälschten
|
||
`internal/migrate`s ungefilterte `registry.List()`-Abfrage im selben
|
||
Prozess/derselben Datenbank.
|
||
|
||
Behoben: `adminPool.Close()` in beiden Tests über `t.Cleanup` statt `defer`
|
||
registriert (vor der Lösch-Cleanup, sodass LIFO die richtige Reihenfolge
|
||
erzwingt – Pool bleibt bis nach dem Löschen offen).
|
||
|
||
### 2.3 `internal/kek/kek_test.go`: derselbe defer/`t.Cleanup`-Fehler, zusätzlich fehlende Zeilen-Bereinigung (Schweregrad **Mittel**, behoben)
|
||
|
||
Dieselbe Fehlerklasse wie 2.2, zusätzlich verschärft: `createTenant` legte
|
||
Zeilen mit `db_dsn='unused'` an, ohne JEMALS eine eigene Aufräumfunktion zu
|
||
registrieren – `setupTest`s `cleanup := func(){ pool.Close() }` schloss nur
|
||
den Pool, löschte aber nie die angelegten `tenants`/`tenant_keks`-Zeilen.
|
||
|
||
Behoben: `createTenant` registriert jetzt eine eigene `t.Cleanup`, die
|
||
Zeile+abhängigen `tenant_keks`-Eintrag löscht; `setupTest` schließt den Pool
|
||
über `t.Cleanup` (nicht mehr über den zurückgegebenen `cleanup()`, den
|
||
Aufrufer per `defer` aufriefen) – damit läuft die Lösch-Cleanup (später
|
||
registriert) vor dem Pool-Schließen (früher registriert).
|
||
|
||
### 2.4 `internal/migrate/migrate_test.go`: hartkodiertes Testschema ohne Lifecycle-Spalten (Schweregrad **Mittel**, behoben)
|
||
|
||
`setupOrchestratorTest` legte `tenants` über ein eigenes,
|
||
hartkodiertes `CREATE TABLE IF NOT EXISTS` ohne die Spalten
|
||
`previous_status`/`deletion_scheduled_at` an, die TEN-04
|
||
(`internal/tenant/registry.go`, `GetBySlug`/`List`) inzwischen mitliest.
|
||
Lief das Paket ISOLIERT (Tabelle wird von diesem Test selbst frisch
|
||
angelegt), fiel der Fehler nicht auf; erst im vollen Testlauf – bei dem die
|
||
Tabelle bereits über die echten Registry-Migrationen existiert – griff
|
||
`CREATE TABLE IF NOT EXISTS` nicht mehr und der Fehler
|
||
`column "previous_status" does not exist` trat zutage.
|
||
|
||
Behoben: fehlende Spalten im Testschema ergänzt (kein Produktionscode
|
||
betroffen).
|
||
|
||
## 3. Build/Test-Ergebnis auf 192.168.1.131 (`/root/nexarch-code-qa04`)
|
||
|
||
```
|
||
go mod tidy -> clean
|
||
go build ./... -> clean
|
||
go vet ./... -> clean
|
||
go test ./... -p 1 -count=1 -> 29/29 Pakete ok, 0 Fehlschläge
|
||
```
|
||
|
||
Enthält insbesondere `internal/pentest` (12 neue Angriffstests, alle
|
||
bestanden) sowie die bereits bestehenden 124 Tests aus QA-02/QA-03 und alle
|
||
Tests der 10 frisch gemergten Vorbedingungs-Branches.
|
||
|
||
## 4. Gesamtergebnis
|
||
|
||
**Bestanden.** Alle vier Akzeptanzkriterien durch gezielte, dokumentierte
|
||
Angriffsversuche mit Ergebnisprotokoll erfüllt; alle drei Pflichtprüfungen
|
||
durchgeführt und protokolliert (Abschnitte 1.2–1.4). Vier real gefundene
|
||
Fehler (Abschnitt 2, Schweregrad Hoch/Mittel) behoben – ausschließlich
|
||
Testinfrastruktur, kein Produktionscode verändert.
|