CI / pruefen (push) Canceled after 0s
Geprueft gegen ASVS und die Cheat Sheets zu Authentication, Session Management und Password Storage. Das meiste stand schon gut da – Token mit 256 Bit und nur als Hash in der Datenbank, serverseitige Pruefung bei jeder Anfrage, Sitzungen weg beim Passwortwechsel, TOTP mit Replay-Schutz. Fuenf Punkte blieben. **Aufwand beim Hashen.** N war 2^15 bei p=1. Das Password Storage Cheat Sheet nennt 2^17/p=1 als Minimum und listet als gleichwertig unter anderem 2^16/p=2 und 2^15/p=3 – mit p=1 lag 2^15 unter *allen* zulaessigen Kombinationen und war rund dreimal billiger zu knacken als vorgesehen. Jetzt 2^17/p=1, gemessen 0,5 s und 128 MiB je Pruefung. Der Speicher ist gedeckelt: Node rechnet scrypt im libuv-Threadpool, also hoechstens vier parallel. **Neuhashen bei der Anmeldung.** Ohne das blieben bestehende Passwoerter fuer immer auf den alten Parametern – anheben laesst er sich nur dort, wo das Klartextpasswort vorliegt. needsRehash vergleicht den Gesamtaufwand N*r*p, nicht Feld fuer Feld, damit gleichwertige Kombinationen nicht grundlos neu gehasht werden. **Aufzaehlbarkeit.** Drei Zweige beantworteten sich *vor* der Passwort- pruefung und verrieten damit die Existenz eines Kontos: gesperrt (423), deaktiviert (403) und "kein Passwort hinterlegt, nimm einen Passkey" (401). Der einheitliche Text GENERIC war damit wirkungslos. Jetzt wird zuerst das Passwort geprueft; Klartext gibt es erst danach. Reine Passkey-Konten verbrennen dieselbe Rechenzeit, damit sich der Fall auch nicht an der Dauer zeigt. Der Preis ist der verlorene Hinweis "nimm den Passkey" – bewusst hingenommen. **Leerlauffrist.** lastSeenAt wurde gefuehrt, aber nie ausgewertet: ein abgegriffener Cookie galt bis zu 30 Tage, egal ob das Konto benutzt wurde. ASVS 3.3.2 will beide Grenzen. Acht Stunden, ueber SESSION_IDLE_HOURS einstellbar. Das Schreibintervall fuer lastSeenAt haengt jetzt an der Frist – bei fester Stunde haette eine kuerzere Frist sich selbst ausgehebelt. **Cookies.** SameSite Strict statt Lax, und bei https der Praefix "__Host-". Der laesst den Browser erzwingen, dass kein Domain-Attribut gesetzt ist – eine andere Subdomain kann den Cookie dann nicht ueberschreiben. Beides kostet einmalig eine neue Anmeldung. Dazu die Passwortpruefung: Wiederholungen, durchlaufende Zeichenfolgen und Passwoerter, die den Benutzernamen enthalten, werden abgelehnt. Zusammensetzungsregeln bleiben bewusst aus – ASVS 2.1.9 raet davon ab. Geprueft: 232 Unit-Tests (davon 12 neue zu Aufwand, Neuhashen und Mustern) und 30 E2E-Tests, darunter einer, der die Antwort auf ein stillgelegtes Konto gegen die auf ein erfundenes haelt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
71 lines
2.2 KiB
JSON
71 lines
2.2 KiB
JSON
{
|
||
"name": "nebula",
|
||
"version": "1.8.0",
|
||
"private": true,
|
||
"description": "Nebula – Dashboard fuer Vibe-Coding Projekte mit Git-Anbindung (GitHub, GitLab, Gitea)",
|
||
"scripts": {
|
||
"dev": "next dev",
|
||
"build": "prisma generate && next build",
|
||
"start": "next start",
|
||
"lint": "next lint",
|
||
"typecheck": "tsc --noEmit",
|
||
"db:generate": "prisma generate",
|
||
"db:migrate": "prisma migrate deploy",
|
||
"db:migrate:dev": "prisma migrate dev",
|
||
"db:push": "prisma db push",
|
||
"db:studio": "prisma studio",
|
||
"db:seed": "prisma db seed",
|
||
"db:reset": "prisma migrate reset",
|
||
"docker:db": "docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d db",
|
||
"docker:db:down": "docker compose stop db",
|
||
"docker:up": "docker compose --profile full up -d --build",
|
||
"docker:down": "docker compose --profile full down",
|
||
"docker:logs": "docker compose --profile full logs -f app",
|
||
"docker:tools": "docker compose --profile tools up -d",
|
||
"setup": "node scripts/setup.mjs",
|
||
"postinstall": "prisma generate",
|
||
"test": "vitest run",
|
||
"test:watch": "vitest",
|
||
"test:e2e": "npm run build && playwright test",
|
||
"test:e2e:ui": "playwright test --ui",
|
||
"test:all": "npm test && npm run test:e2e"
|
||
},
|
||
"dependencies": {
|
||
"@dnd-kit/core": "^6.3.1",
|
||
"@dnd-kit/modifiers": "^9.0.0",
|
||
"@dnd-kit/sortable": "^10.0.0",
|
||
"@prisma/client": "^6.8.2",
|
||
"@simplewebauthn/browser": "^13.3.0",
|
||
"@simplewebauthn/server": "^13.3.2",
|
||
"lucide-react": "^0.511.0",
|
||
"next": "^15.3.3",
|
||
"nodemailer": "^9.0.3",
|
||
"qrcode": "^1.5.4",
|
||
"react": "^19.1.0",
|
||
"react-dom": "^19.1.0",
|
||
"react-markdown": "^10.1.0",
|
||
"rehype-sanitize": "^6.0.0",
|
||
"remark-gfm": "^4.0.1",
|
||
"zod": "^3.25.28"
|
||
},
|
||
"devDependencies": {
|
||
"@playwright/test": "^1.62.0",
|
||
"@tailwindcss/postcss": "^4.1.7",
|
||
"@types/node": "^22.15.21",
|
||
"@types/nodemailer": "^8.0.1",
|
||
"@types/qrcode": "^1.5.6",
|
||
"@types/react": "^19.1.5",
|
||
"@types/react-dom": "^19.1.5",
|
||
"axe-core": "^4.12.1",
|
||
"dotenv": "^17.4.2",
|
||
"prisma": "^6.8.2",
|
||
"tailwindcss": "^4.1.7",
|
||
"tsx": "^4.19.4",
|
||
"typescript": "^5.8.3",
|
||
"vitest": "^4.1.10"
|
||
},
|
||
"engines": {
|
||
"node": ">=20.11"
|
||
}
|
||
}
|