Files
Florian Wilde c49810d092 feat: Feldverschluesselung, Argon2id und TOTP-Zweitfaktor
Die Sicherheitsschicht, auf der alles Weitere aufsetzt.

Feldverschluesselung (AES-256-GCM):
- Umschlagformat bc1.<version>.<iv>.<tag>.<ct>. Die Schluesselversion
  erlaubt Rotation, ohne alte Daten unlesbar zu machen.
- Der Feldname wird als AAD mitauthentifiziert. Ein Geheimtext laesst
  sich damit nicht von diagnosen nach notizen umhaengen.
- Leere Werte werden zu NULL statt zu einem Umschlag - sonst verriete
  die Datenbank, dass ueberhaupt etwas eingetragen war.
- VERSCHLUESSELTE_FELDER ist der einzige Ort, an dem steht, was
  verschluesselt gehoert. sealen()/oeffnen() arbeiten darauf.

Argon2id nach OWASP-Empfehlung (19 MiB, t=2, p=1), mit hashVeraltet()
fuer spaetere Parametererhoehung ohne Migration.

Blindindex per HMAC-SHA256 mit eigenem Schluessel: erlaubt die Frage
"kenne ich diese IBAN schon?", ohne sie im Klartext zu halten.
Derselbe Mechanismus pseudonymisiert IP-Adressen fuers Audit-Log.

TOTP mit otpauth, QR-Code serverseitig als Data-URI - kein externer
QR-Dienst, kein CDN. Wiederherstellungscodes aus einem Alphabet ohne
0/O und 1/I, einzeln Argon2id-gehasht, Pruefung ohne fruehen Abbruch.

37 Tests decken Rundlauf, Manipulation, Feldbindung, Rotation,
Salzung und Normalisierung ab.

Etappe: 1
Phase: Fundament
2026-08-22 01:42:13 +02:00

21 lines
578 B
TypeScript

import { defineConfig } from 'vitest/config'
import { fileURLToPath } from 'node:url'
export default defineConfig({
test: {
environment: 'node',
include: ['tests/**/*.test.ts'],
setupFiles: ['./tests/setup/umgebung.ts'],
},
resolve: {
alias: [
{ find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) },
// "server-only" wirft absichtlich beim Import ausserhalb des Servers.
{
find: /^server-only$/,
replacement: fileURLToPath(new URL('./tests/setup/leer.ts', import.meta.url)),
},
],
},
})