Files
nexarch/docs/QA-04-PRUEFPROTOKOLL.md
T
sysopsandClaude Sonnet 5 9f641fec76 QA-04: sicherheits-penetrationspruefung-core
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
2026-08-29 09:57:36 +02:00

8.9 KiB
Raw Blame History

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 RotateMasterKeys 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/migrates 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 setupTests 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.21.4). Vier real gefundene Fehler (Abschnitt 2, Schweregrad Hoch/Mittel) behoben ausschließlich Testinfrastruktur, kein Produktionscode verändert.