Skip to main content

Security Hardening

Authentication

Set API_KEY_CLIENTS to a JSON object mapping each API key to the client id that owns its data. Empty mappings fail closed. API key comparison uses constant-time comparison across the configured mapping.

Failed authentication attempts are rate-limited per source with AUTH_FAILURE_RATE_LIMIT and logged without raw key values. Valid protected requests are rate-limited per client with RATE_LIMIT; 429 responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.

Key Rotation

  1. Add the new key to API_KEY_CLIENTS with the same client id as the old key.
  2. Deploy the updated environment and confirm both keys work during the overlap window.
  3. Move the DMS integration to the new key.
  4. Remove the old key from API_KEY_CLIENTS and redeploy.
  5. Confirm old-key requests return 401 and repeated old-key attempts eventually return 429.

Authorization Header Decision

The API intentionally supports X-API-Key only. Authorization is listed in CORS preflight headers so proxies and browsers can send normal preflight checks, but it is not accepted for API authentication. This avoids ambiguity between API-key auth and future bearer-token schemes.

CORS

CORS_ORIGINS must list explicit origins in production. * is rejected when DEBUG=false. Methods and headers are explicit; credentials are enabled intentionally for browser clients on approved origins.

Upload Validation

Uploads are accepted only when the extension, declared MIME type, and sniffed file content agree with the configured allow-list. Mismatches fail before the file is enqueued so workers do not process disguised binaries or unexpected content types.

OOXML Zip-Bomb Protection

OOXML files (.docx/.pptx/.xlsx) are ZIP archives and are vulnerable to zip-bomb attacks. The system checks two limits before parsing:

  • OFFICE_MAX_UNCOMPRESSED_BYTES (default: ~1GB) — total decompressed size across all archive entries
  • OFFICE_MAX_COMPRESSION_RATIO (default: 200) — ratio of uncompressed to compressed size per entry

Files exceeding either limit are rejected with a clear error before any parser touches the content.

Office MIME Type and Content Sniffing

The modern OOXML formats (.docx/.pptx/.xlsx) all share the ZIP magic bytes (PK\x03\x04), and legacy formats (.ppt/.xls) share the OLE2 compound-file magic (\xd0\xcf\x11\xe0). Content sniffing treats members of each family as compatible since the leading bytes cannot distinguish between them. The parser layer rejects a genuinely wrong inner format.

Audit Logs

Every request receives a correlation id and emits structured audit fields: request id, endpoint, method, status code, latency, client id when authenticated, hashed API-key identifier when known, and error code when present. Upload acceptance and auth failures emit dedicated audit events. Raw API keys and document contents are not logged; user-controlled strings are control-character sanitized before logging.

Go-Live Sign-Off

The OWASP checklist and go-live owner fields remain human-gated. Do not mark them complete until a named security owner, independent runbook walkthrough evidence, first-30-day success metrics, and review rhythm are recorded in the release packet.