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

4.7 KiB
Raw Blame History

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.