Files
safetynet/docs/protected-evidence.md
2026-10-13 10:34:48 +02:00

28 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Protected security evidence
Security analysts need attributable facts, including potentially confidential content. These facts belong in a separately encrypted evidence store, not unrestricted OpenTelemetry attributes or ordinary SIEM payloads. Operational traces remain separate and can refer to an evidence ID.
## Implemented server boundary
`POST /api/device/evidence` authenticates an enrolled, non-revoked device and requires an existing source event UUID belonging to that device. Supported typed records are download source URL, filename, MIME, SHA-256, size and decision; or interaction page URL, action, bounded target details and optionally selected text. No arbitrary JSON, full documents, password fields, authorization fields, cookies or credential-bearing URL are accepted. URLs exclude queries and fragments. Interaction callers must assert `sensitiveField: false`; the future browser capture path must enforce this authoritatively rather than trust page JavaScript.
Before encryption, a bounded second check rejects recognizable credentials in selected text, target labels, filenames and source/page URL paths, including decoded paths. It detects private-key headers, Bearer/Basic authorization, assigned password/API-key/access-token/refresh-token/client-secret values, common OpenAI/GitHub/AWS key formats and JWT-shaped tokens. General confidential company text remains eligible for protected evidence. This is not proof that every unknown password or secret can be recognized: an arbitrary password without context is indistinguishable from ordinary text. Native capture must exclude password and authentication controls independently; the client-provided `sensitiveField` bit alone is insufficient.
Each record is encrypted with AES-256-GCM and a fresh random 96-bit nonce. Authenticated associated data binds format, record ID, source event, device, type, key ID, creation and expiry. SQLite stores ciphertext and metadata only. Source/type uniqueness prevents duplicate replacement. Record insertion and metadata-only audit are transactional. Failure to persist the audit rolls back the insert.
`GET /api/admin/evidence/:id?reason=investigation` requires the explicit `evidence.read` permission, currently assigned only to administrators. Operators and auditors cannot read plaintext. Reasons are the enums `investigation`, `incident-response`, `compliance`. A durable attributable access audit must succeed before a response is returned. Neither plaintext nor free-form reasons appear in ordinary audit logs. HTTP responses inherit no-store, no-referrer and authentication controls.
## Deployment
Configure `EVIDENCE_KEY_FILE` with an absolute path to an independently generated binary file containing exactly 32 random bytes. Never put the key in SQLite, source control, command-line arguments or telemetry. The deployment operator must restrict Windows ACLs to the management service identity and designated recovery custodians. Linked paths are rejected; Unix deployments also reject group/other file permissions. A configured invalid or unreadable key fails startup. Without a configured key, evidence submission and reads return 503. This is intentional, not a plaintext fallback.
`EVIDENCE_RETENTION_DAYS` defaults to 30 and must be 1–365. Expired evidence cannot be read unless its source event belongs to an open or investigating incident. Audit retention preserves evidence source events. Physical ciphertext deletion is not automated in this initial version: expiry limits access but does not claim storage erasure. Key rotation, HSM/KMS custody, cryptographic erasure, legal holds and tested backup/recovery remain production work. Replacing the key without retaining its old version makes old records unreadable; their key IDs are checked explicitly.
## Scope and validation
This implements the server storage and access boundary, not browser human-click capture, settings to enable collection, client delivery or an analyst UI. These must use signed central policy, keep password/authentication fields excluded, bind events to native browser context and label partial coverage. An enrolled device is an authenticated reporter, not proof its endpoint was uncompromised.
`tests/evidence.test.mjs` uses real HTTP and SQLite to verify ciphertext storage, role denials, source ownership/type validation, password/token field and credential-URL rejection, replay rejection, missing-key behavior, authenticated metadata tamper rejection, audit-failure rollback, plaintext response suppression and retention correlation.
The credential-content test additionally verifies that recognizable secrets in free text, labels, filenames and encoded paths return 400 without inserting any evidence rows, while confidential engineering text is accepted.