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 default, with route-level overrides for sensitive auth and billing endpoints.
- Route-level app limits: login, register, and password reset are capped at 5 requests/minute; OAuth, refresh, logout, email verification, and 2FA challenge are capped at 10 requests/minute; verification resend is capped at 3 requests/minute; account export is capped at 2 requests/hour; 2FA setup/verification/recovery/disable flows use 5-minute windows; checkout creation is capped at 5 requests/minute; checkout confirmation is capped at 10 requests/minute.
- Payment webhook boundary: Stripe webhooks are not app-throttled because signature verification, idempotent event handling, and Stripe retry behavior are the primary controls for that public endpoint.
- Current launch posture: Redis is optional. When
REDIS_URLis absent, request throttling uses process-local counters as a soft API guard. For the single-instance MVP launch path, Render's managed Cloudflare-backed DDoS protection covers volumetric attacks while app-layer throttles and Postgres-backed lockouts carry path-specific abuse controls. The current Porkbun + Render stack does not expose customer-configurable path-based WAF rules, so add user-managed WAF rules or shared counters before horizontal API scaling. - Durable brute-force detection: Postgres-backed failed-login tracking protects the launch path without requiring Redis solely for security state. It tracks email and IP attempts, clears email counters on successful login, preserves user-enumeration resistance, and supports 15-minute lockouts.
- 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, GDPR/account exports, inventory CSV exports, 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. Export audit entries record metadata counts and scope rather than the exported payload itself.
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. Only events already marked successful are treated as final duplicates, so a failed processing attempt can be retried safely instead of being skipped forever.
- 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, provider metadata, and explicit processing status/error fields for compliance, debugging, and admin operations follow-up.
- 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.
- Postgres-backed persistent brute-force detection across IPs.
Roadmap Enhancements
✅ Completed (Dec 2024)
- ✅ Optional Redis rate-limit adapter with automatic in-memory 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).
- ✅ Postgres-backed persistent brute-force detection across email and IP subjects.
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).
- Edge/provider rate-limit proof for auth and billing endpoints is complete as a documented current-provider limitation; revisit before horizontal API scaling or if abuse volume rises.
- 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.