21 Security & Privacy Model
Purpose
Establish the security and privacy guardrails for the inventory app so the product is safe-by-default, portable across hosting providers, and trustworthy for episodic use cases.
Threat Model
Primary risks considered for the MVP:
- Broken access control exposing another user’s inventory.
- Session theft via XSS or insecure cookie handling.
- Credential stuffing and brute-force attacks on login endpoints.
- Sensitive data leaking through logs or analytics tooling.
- Unsafe file uploads (malware or public buckets).
- Confusing users with stale offline data.
Out of scope for the MVP (tracked for later): automated fraud detection, enterprise SSO/MFA, and external penetration tests.
Authentication
- Auth flow uses JWT access tokens (15 minutes) and refresh tokens (14 days) stored in httpOnly cookies.
- Secrets: JWT_ACCESS_SECRET and JWT_REFRESH_SECRET must be 32+ random bytes; rotate independently.
- Cookies are issued with httpOnly, Secure (when NODE_ENV=production), and SameSite=lax defaults. COOKIE_DOMAIN allows subdomain sharing in staging/prod.
- Refresh on every successful login/refresh ensures stolen refresh tokens are rotated quickly.
- Optional time-based one-time passwords (TOTP) add a second factor. Users scan a 15-minute setup QR, confirm a 6-digit code, and receive eight hashed, single-use recovery codes. Disabling or regenerating codes requires password + code verification and revokes all refresh tokens. Events are audit logged.
Rationale: cookie-based tokens avoid localStorage theft risks and keep future native clients viable.
Authorization
- Every query scopes by both identifier and userId (
{ id, userId }) to prevent horizontal access. - Aggregations (counts, search) use the authenticated user’s scope before running operations.
- Database-level foreign keys, unique constraints, and quantity check constraints add defense in depth.
Web Security Controls
- CORS: FRONTEND_ORIGIN env var enforces an explicit allowlist; credentials are enabled to permit cookies.
- CSRF: double-submit cookie pattern via
/auth/csrf; state-changing routes requireX-CSRF-Token. Safe requests echo the next token header. - Content Security Policy: Configured in Next.js with environment-aware directives. Restricts resource loading to same-origin and explicitly whitelisted sources (API backend, HTTPS images). Prevents clickjacking with
frame-ancestors 'none'. Blocks unauthorized scripts, styles, and embedded content. - Security Headers:
X-Frame-Options: DENY- Prevents clickjacking attacksX-Content-Type-Options: nosniff- Prevents MIME-sniffing vulnerabilitiesReferrer-Policy: strict-origin-when-cross-origin- Controls referrer information leakagePermissions-Policy- Disables camera, microphone, geolocation accessStrict-Transport-Security- Enforces HTTPS in production (1 year max-age with preload)
- Helmet.js: Backend API uses Helmet middleware for additional HTTP security headers with PWA-friendly configuration.
Rate Limiting & Abuse Prevention
- Nest ThrottlerModule enforces two buckets:
global: 120 requests/minute default to cap bursts.auth: 10 requests/minute covering login/register/refresh.
- Persistent Brute-Force Detection (Redis-backed):
- Tracks failed login attempts by email address (max 5 attempts before 15-minute lockout)
- Tracks failed login attempts by IP address (max 10 attempts across all accounts before 15-minute lockout)
- Exponential backoff: Delays increase exponentially after each failed attempt (1s → 2s → 4s → 8s → 16s → 30s max)
- Account lockout: After 5 failed attempts, account is locked for 15 minutes
- IP lockout: After 10 failed attempts from a single IP, all logins from that IP are blocked for 15 minutes
- Automatic expiration: Failed attempt counters auto-expire after lockout period
- Persistent across restarts: Uses Redis for distributed protection across multiple server instances
- Clear on success: Failed attempt counters are cleared on successful login
- Prevents user enumeration: Same error message and timing for non-existent users vs wrong passwords
- 429 responses include structured errors so the frontend can display retry guidance.
Input Validation & Error Handling
- Global ValidationPipe enforces DTO schemas, strips unknown fields, and converts primitives.
- Invalid requests raise
VALIDATION_ERRORwith per-field details; errors are sanitized before returning to clients. - Central HTTP exception filter standardizes error envelopes and prevents stack traces from leaking.
Password Policy
- Passwords hashed with bcrypt cost 12.
- Minimum length: 10 characters (enforced).
- Complexity requirements: must include uppercase, lowercase, number, and special character.
- Pattern detection prevents sequential characters and common weak passwords.
- Password change flow prevents reusing the current password and throttles error feedback.
- Register/login normalize email casing and trim whitespace to avoid duplicate accounts.
- Real-time password strength feedback shown during registration.
Logging & Observability
- StructuredLogger emits JSON with timestamp, level, event name, and optional context data.
- Logs exclude passwords, tokens, cookies, and full request bodies. Request IDs propagate via middleware for traceability.
- LOG_LEVEL env var controls verbosity (default
log); production typically useswarn. - Audit Logging: Security-sensitive operations (account deletion, password changes, item/location deletion) are logged to a dedicated AuditLog table with userId, timestamp, IP address, user agent, and old/new values for 90-day retention and compliance.
File Upload Security
- Uploads use S3-compatible storage through signed URLs. Buckets remain private; only pre-signed PUT/GET operations are allowed.
- Allowed mime-types: image/jpeg, image/png, image/webp (enforced during validation). Maximum size is bounded at the upload layer.
- Stored metadata: key, mime, size; URLs exposed to clients are time-limited.
Payment Webhook Security
- Signature Verification: All payment webhooks (Stripe) validate cryptographic signatures using the provider's SDK before processing events. Invalid signatures are rejected immediately with 400 Bad Request.
- Idempotency: Webhook events are deduplicated using external event IDs stored in the WebhookEvent table with unique constraints. Duplicate events return 200 OK but skip processing to ensure reliable payment state.
- Raw Body Preservation: NestJS configured with
rawBody: trueto preserve the original request body required for signature verification while maintaining JSON parsing for other endpoints. - Event Audit Trail: All webhook events (successful and failed) are logged to the database with full payload, processing timestamps, and provider metadata for compliance and debugging.
- Secure Endpoint: Webhook endpoints are public (no auth required) but protected by signature verification. Stripe Webhook Secret must be obtained from the dashboard after endpoint configuration.
- Async Processing: Events are processed synchronously for payment-critical operations (subscription creation, payment success) but designed for future async retry patterns if needed.
Rationale: Payment webhooks are critical security boundaries - compromised webhooks could enable fraudulent subscriptions. Signature verification ensures events originate from the payment provider, while idempotency prevents duplicate charges or account modifications.
Privacy & Data Minimization
- Data stored: account email, inventory entities, optional item photos.
- Excluded from storage: GPS coordinates, device identifiers, raw search queries. Analytics capture only aggregate metrics (query length, result count, success flag).
- Offline cache (IndexedDB) improves UX but leaves data on the device; signing out should clear cached data and we recommend device-level security for shared devices.
Secure Defaults Checklist
- Credentialed CORS allowlist enforced with FRONTEND_ORIGIN.
- CSRF protection active on all state-changing endpoints.
- httpOnly cookies with environment-aware Secure flag.
- DTO validation + whitelist to block mass-assignment.
- Rate limiting on auth endpoints.
- Private, signed upload flow.
- Structured logging without secrets.
- Content Security Policy configured with resource restrictions.
- Security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy).
- HSTS enabled for HTTPS enforcement in production.
- Payment webhook signature verification with idempotency protection.
- Persistent brute-force detection across IPs with exponential backoff.
Roadmap Enhancements
✅ Completed (Dec 2024)
- ✅ Redis-based rate limiting for distributed environments with automatic fallback.
- ✅ Environment variable validation on startup with clear error messages.
- ✅ Password strength requirements (10+ chars, complexity) with real-time UI feedback.
- ✅ Session management UI (view and revoke active sessions).
- ✅ Time-based two-factor authentication with recovery codes and audit trails.
- ✅ Comprehensive audit logging (item/location deletions, password changes, account deletion).
- ✅ Content Security Policy with environment-aware directives and security headers.
- ✅ Email verification flow with 24-hour token expiration and resend capability.
- ✅ Account takeover protection (revoke all refresh tokens on password change, database-backed token validation).
- ✅ Payment webhook security (Stripe signature verification, idempotency, audit trail).
- ✅ Legal compliance (Privacy Policy, Terms of Service, Cookie Policy, GDPR data export, complete account deletion with S3 cleanup).
- ✅ Persistent brute-force detection across IPs with exponential backoff and automatic lockout.
High Priority (Pre-Public Beta)
- Automated malware scanning for uploads.
- Enhanced webhook retry mechanism with exponential backoff.
For Paid Launch
- ✅ Email verification flow (completed).
- ✅ Payment webhook security (completed).
- ✅ Account takeover protection (completed).
- ✅ Legal compliance (Privacy Policy, Terms of Service, Cookie Policy, GDPR data export, complete account deletion with S3 cleanup) (completed).
- ✅ Persistent brute-force detection (completed).
- Payment webhook security (Stripe/Paddle).
- Account takeover protection (revoke all sessions on password change).
- Legal compliance (privacy policy, ToS, GDPR data export).
See Production Security Checklist for detailed implementation plan.
Locked Decisions
- Continue using cookie-based JWT tokens with refresh rotation.
- Maintain strict per-user authorization checks at every data layer.
- Default to private storage buckets with signed access patterns.
- Avoid collecting raw, user-identifiable analytics without explicit consent.
- Use managed hosting (Render) with auto-SSL for initial deployment.
- Ship authenticator-based 2FA before adding SMS/voice factors.