security: Audit-Log für alle Klartext-Passwort-Reads (CRITICAL)

Pentest-Finding "Klartext-Passwörter über API abrufbar (HIGH, post-auth)"
adressiert: reversible Verschlüsselung der Anbieter-/Portal-Logins ist
by-design (Feature "Login anzeigen" braucht sie zwingend), aber jeder
einzelne Decrypt-Vorgang muss im Audit-Log nachvollziehbar sein. Bisher
schrieb KEINER der 6 betroffenen Endpoints einen Eintrag.

Behoben in:
- getPortalPassword (Customer-Portal-Login)
- getContractPassword (Anbieter-Login z.B. Vattenfall, EWE, …)
- getSimCardCredentials (PIN/PUK)
- getInternetCredentials (DSL-Login)
- getSipCredentials (Telefon-/VoIP-Login)
- getMailboxCredentials (Stressfrei-IMAP/SMTP)

Alle nutzen `action: 'READ'` mit eigenem ResourceType + Sensitivity
CRITICAL via determineSensitivity-Map. Label nennt explizit
"Klartext … entschlüsselt" + Resource-ID, damit im AuditLog-Viewer
auf einen Blick erkennbar ist, wer wann welches Passwort eingesehen
hat (DSGVO + Insider-Threat-Erkennung).

Live verifiziert: nach Klick auf getPortalPassword erscheint im
AuditLog der Eintrag "READ PortalPassword CRITICAL – Klartext-Portal-
Passwort von Kunde #1 entschlüsselt".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-16 15:33:26 +02:00
parent e2cd26a29e
commit 0943f11999
5 changed files with 79 additions and 1 deletions
+13 -1
View File
@@ -986,7 +986,19 @@ export async function setPortalPassword(req: Request, res: Response): Promise<vo
export async function getPortalPassword(req: Request, res: Response): Promise<void> {
try {
const password = await authService.getCustomerPortalPassword(parseInt(req.params.customerId));
const customerId = parseInt(req.params.customerId);
const password = await authService.getCustomerPortalPassword(customerId);
// Klartext-Passwort-Read auditieren (CRITICAL): wer hat wann das Portal-
// Passwort eines Kunden entschlüsselt? Wichtig für DSGVO-Nachvollziehbarkeit
// + Insider-Threat-Erkennung.
await logChange({
req,
action: 'READ',
resourceType: 'PortalPassword',
resourceId: customerId.toString(),
label: `Klartext-Portal-Passwort von Kunde #${customerId} entschlüsselt`,
customerId,
});
res.json({ success: true, data: { password } } as ApiResponse);
} catch (error) {
res.status(500).json({