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]>
36 lines
1.3 KiB
TypeScript
36 lines
1.3 KiB
TypeScript
const SIGNATURE_CHECKS: Record<string, (buffer: Buffer) => boolean> = {
|
|
'image/png': (buffer) =>
|
|
buffer.length >= 8 &&
|
|
buffer
|
|
.subarray(0, 8)
|
|
.equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])),
|
|
'image/jpeg': (buffer) =>
|
|
buffer.length >= 3 &&
|
|
buffer[0] === 0xff &&
|
|
buffer[1] === 0xd8 &&
|
|
buffer[2] === 0xff,
|
|
'image/webp': (buffer) =>
|
|
buffer.length >= 12 &&
|
|
buffer.subarray(0, 4).toString('ascii') === 'RIFF' &&
|
|
buffer.subarray(8, 12).toString('ascii') === 'WEBP',
|
|
'application/pdf': (buffer) =>
|
|
buffer.length >= 5 && buffer.subarray(0, 5).toString('ascii') === '%PDF-',
|
|
};
|
|
|
|
/**
|
|
* Verifies a file's actual bytes match its declared MIME type instead of
|
|
* trusting the client-supplied value alone (a multipart Content-Type header
|
|
* or a data: URL prefix are both attacker-controlled). Used at every upload
|
|
* path that persists the bytes to disk under a server-chosen name/extension,
|
|
* so a mislabeled file can't be dropped onto the filesystem as a type it
|
|
* isn't. Types with no signature defined here are not checked (returns
|
|
* true) - callers still gate on their own MIME allowlist first.
|
|
*/
|
|
export function matchesFileSignature(
|
|
buffer: Buffer,
|
|
declaredMimeType: string,
|
|
): boolean {
|
|
const check = SIGNATURE_CHECKS[declaredMimeType];
|
|
return check ? check(buffer) : true;
|
|
}
|