Back to docs hub·docs/architecture/21-security-privacy.md
Reference document

21 Security & Privacy Model

Last updated Mar 23, 2026

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:

  1. Broken access control exposing another user’s inventory.
  2. Session theft via XSS or insecure cookie handling.
  3. Credential stuffing and brute-force attacks on login endpoints.
  4. Sensitive data leaking through logs or analytics tooling.
  5. Unsafe file uploads (malware or public buckets).
  6. 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 require X-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 attacks
    • X-Content-Type-Options: nosniff - Prevents MIME-sniffing vulnerabilities
    • Referrer-Policy: strict-origin-when-cross-origin - Controls referrer information leakage
    • Permissions-Policy - Disables camera, microphone, geolocation access
    • Strict-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_ERROR with 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 uses warn.
  • 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: true to 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.