Public demo passwords are intentional for this open-source portfolio. Change all secrets before any real production deployment. See the root README for the current demo account table.
- bcrypt password hashing (cost 12)
- JWT access + refresh token rotation
- Refresh token in HttpOnly cookie (
SameSite=Strictlocally;SameSite=None; Securein production for Cloudflare ↔ Render) - Account lockout after repeated failed logins (unlock via SQL or re-seed)
- Stored in private filesystem path (dev) / encrypted object storage (prod)
- Only metadata in PostgreSQL (
storage_key, hash, mime, size) - Never exposed via public APIs
- Admin access via short-lived signed URLs with audit logging
- Retention: delete rejected docs after 90 days
- Parameterized SQL only
- Zod validation on all endpoints
- Helmet, CORS whitelist, rate limiting (API + stricter auth endpoints)
- No stack traces in production
- Sanitize user-generated content (messages, reviews)
- Magic-byte validation (not MIME header alone)
- Size limits enforced server-side
- Random UUID filenames
- Product images public; identity docs private
- RBAC: USER, ADMIN
- IDOR prevention in service layer on every resource access
- Admin actions logged in
audit_logs