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
- Add the new key to
API_KEY_CLIENTSwith the same client id as the old key. - Deploy the updated environment and confirm both keys work during the overlap window.
- Move the DMS integration to the new key.
- Remove the old key from
API_KEY_CLIENTSand redeploy. - 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 entriesOFFICE_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.