fix: SSO-MFA-Umgehung, Datei-Magic-Bytes und oeffentliches Swagger geschlossen

Weitere vom Sicherheitsaudit gemeldete Luecken behoben, die bisher als
niedrige Prioritaet offen gelassen wurden:

- SSO-Login (Entra ID) prüft jetzt lokale MFA genauso wie der
  Passwort-Login: ist mfaEnabled gesetzt, liefert /auth/sso/complete
  denselben mfaRequired-Codepfad wie /auth/login, bevor Tokens
  ausgestellt werden. Frontend (Login-Seite, AuthContext) zeigt in dem
  Fall den bestehenden Code-Eingabe-Schritt statt die Anmeldung
  stillschweigend abzuschliessen.
- Avatar- und Dokument-Uploads pruefen jetzt die tatsaechlichen
  Datei-Bytes (Magic Numbers fuer PNG/JPEG/WEBP/PDF) statt nur dem
  client-angegebenen MIME-Typ/data:-URL-Praefix zu vertrauen.
- Swagger-UI (/api/v1/docs) ist jetzt standardmaessig deaktiviert
  (ENABLE_SWAGGER=true erforderlich), da sie zuvor das komplette
  API-Schema ungeschuetzt oeffentlich preisgab.

npm-audit-Warnungen (deepmerge-ts/mysql2 unter prisma) bewusst nicht
gefixt: kein stabiles Prisma-Release loest sie bislang auf, nur eine
Release-Candidate-Hauptversion - das Risiko eines Downgrades/RC-Upgrades
der Datenbankschicht uebersteigt den Nutzen fuer eine Kette, die zur
Laufzeit ohnehin nicht erreichbar ist (nur Prisma-CLI, nur MySQL-Treiber,
dieses Projekt nutzt Postgres).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Sonnet 5 committed 2026-09-02 09:09:58 +02:00
1 parent 1b16b49913
commit b7ff92abd3
16 files changed
+225 -28

No files matched your search

@@ -8,6 +8,7 @@ import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { PrismaService } from '../prisma/prisma.service';
import { AuditLogService } from '../audit/audit-log.service';
import { matchesFileSignature } from '../common/file-signature';
import { Document, DocumentEntityType } from '../../generated/prisma';
import { ALLOWED_DOCUMENT_MIME_TYPES } from './document-mime-types';
@@ -86,6 +87,11 @@ export class DocumentsService {
if (!(file.mimetype in ALLOWED_DOCUMENT_MIME_TYPES)) {
throw new BadRequestException('Nicht unterstützter Dateityp');
}
if (!matchesFileSignature(file.buffer, file.mimetype)) {
throw new BadRequestException(
'Datei entspricht nicht dem angegebenen Format',
);
}
await this.assertEntityExists(entityType, entityId);
const extension = ALLOWED_DOCUMENT_MIME_TYPES[file.mimetype];