Skip to content

Security: slateeho/taskboards

Security

docs/SECURITY.md

Security

Authentication

Email/Password

  • Passwords hashed with bcrypt (cost factor 10)
  • Stored in users.password column
  • Login: POST /auth/login returns user object (no token — session tracked by user ID)

Google OAuth 2.0

  • Uses google-auth-library with OpenID Connect
  • Scopes: openid, email, profile
  • Auto-creates user on first login

Session Model

  • Frontend stores user object in localStorage
  • All API requests include X-User-Id header
  • Auth guard middleware validates user exists in DB
  • No server-side session tokens or JWTs

Message Encryption (AES-256-GCM)

All chat messages (task, group, direct) encrypted at rest.

Parameter Value
Algorithm AES-256-GCM
Key size 32 bytes (from MSG_ENCRYPTION_KEY env, hex-encoded)
IV 16 bytes random per message
Storage body = hex ciphertext, iv = hex(IV) + ":" + hex(AuthTag)

Legacy messages with iv = NULL returned as plaintext (backward compatible).

File Encryption (AES-256-GCM)

All uploaded files encrypted before storage in file_blobs table.

Parameter Value
Algorithm AES-256-GCM
Key size 32 bytes (from FILE_ENCRYPTION_KEY_BASE64 env, base64-encoded)
IV 12 bytes random per file
Storage encrypted_data (BLOB), iv (BLOB), auth_tag (BLOB)

Files are decrypted on-the-fly when downloaded or viewed.

File Integrity

  • SHA-256 hash computed and stored for every uploaded file (files.sha256)
  • Used for deduplication detection and integrity verification

Message Deletion Audit

When messages are soft-deleted:

  1. deleted_at timestamp set
  2. deleted_by user ID recorded
  3. chat_events entry created with:
    • SHA-256 hash of plaintext body
    • Body length
    • Actor information
  4. Original encrypted body preserved for admin audit access

HTTP Security

  • Helmet — Sets security headers (X-Content-Type-Options, X-Frame-Options, etc.)
  • CSP disabled — Content Security Policy off (single-origin SPA)
  • CORS — Open (all origins allowed)
  • Trust proxy — Enabled for correct IP detection behind reverse proxy
  • JSON limit — 1 MB request body limit
  • File limits — 10 MB (legacy disk), 50 MB (encrypted DB)

Input Validation

  • All request bodies validated with Zod schemas
  • Type coercion and constraint checking before database operations
  • SQL injection prevented by parameterized queries (better-sqlite3 prepared statements)

Access Control

  • Auth guard middleware on all API routes (except public endpoints)
  • No role-based access control at API level — all authenticated users have equal access
  • File "hide" allows per-user visibility control without deleting shared resources

Key Management

Keys are read from environment variables at startup:

  • Never logged or exposed in API responses
  • Missing FILE_ENCRYPTION_KEY_BASE64 throws at runtime on file operations
  • Missing MSG_ENCRYPTION_KEY defaults to null key (zero-filled) — must be set in production

Recommendations

  • Set strong, unique values for both encryption keys before deploying
  • Use a reverse proxy (nginx) with HTTPS termination
  • Restrict network access to the server port
  • Back up data/app.db regularly (contains all data including encrypted blobs)
  • Rotate keys requires re-encryption of all stored data

There aren't any published security advisories