Files
web/docs/THREAT_MODEL.md
T
gamertan 494b72fa3b
verify / verify (push) Successful in 4m29s
Release v0.1.0-preview.28: transactional account email
Reviewed source export adds verified TLS mail, encrypted outbox, mailbox verification and password reset protocols. Preserve public ancestry; omit local development history and operational queue. Consumer deployment and inbox delivery proof remain separate.
2026-09-11 04:12:17 -04:00

93 lines
5.5 KiB
Markdown

<!-- SPDX-License-Identifier: MPL-2.0 -->
# Threat model
The toolkit treats the public network, forwarding headers, request targets,
cookies, credentials, and stored request records as untrusted. Application code,
the configured trusted-proxy set, server filesystem permissions, and explicitly
selected storage adapters are trusted.
Controls include explicit proxy trust, bounded parsing, cryptographic request
and session identifiers, digest-only session storage, Argon2id passwords,
constant-time comparisons, same-origin and CSRF primitives, fail-closed storage
errors, separate safe/sensitive analytics projections, organization-scoped
bindings, single-use invitation digests, and short-lived audited break-glass
grants.
Passkey ceremonies require one exact HTTPS origin and relying-party ID,
discoverable credentials, user verification, and single-use random challenges.
The SQLite adapter consumes enrollment tokens and ceremonies atomically.
Operation approvals retain a separate digest of canonical application state;
the random WebAuthn challenge is never replaced by a predictable state hash.
Cross-origin ceremonies, unsupported credential algorithms, expired state, and
binding drift fail closed. Signature-counter clone warnings are surfaced for
audit but do not automatically lock out multi-device passkeys whose counters
legitimately remain zero.
An application may create an account with a cryptographically generated
temporary credential and `RequirePasswordChange`. Successful rotation compares
the current credential, replaces its Argon2id hash, clears the requirement, and
revokes every session in one repository transaction. The application must
restrict such a principal to password change and logout until rotation succeeds;
the library does not infer route policy. Temporary credentials must be written
to a private channel or mode-`0600` file and must never be printed into logs,
manifests, process arguments, or deployment state.
Administrative recovery is deliberately a separate capability. The storage
adapter atomically replaces the credential, restores the password-change
requirement, revokes all sessions, and appends a generic audit event. The core
library does not expose a recovery HTTP handler, deliver the credential, or
authorize the local operator. Applications must keep that command local,
generate the credential cryptographically, and write it only to a newly created
private file. A recovery must not reveal whether an account exists through a
public request surface.
Passkey-only recovery never creates a password or remote fallback. A local
administrator revokes sessions and active ceremonies and issues a short-lived
single-use enrollment token. Existing passkeys remain visible so the operator
can review and remove lost credentials after enrolling a replacement. The
library prevents remote removal of the final credential; applications should
require a freshly bound passkey assertion before every removal.
Unsafe methods without an exact Origin or trustworthy same-origin Fetch
Metadata fail the origin check. Authentication middleware fails closed when its
service or `__Host-` cookie policy is invalid. Imported request records have
bounded byte and duration fields before analytics sums them.
The toolkit does not sandbox application handlers, secure an incorrectly
configured reverse proxy, authorize application routes automatically, encrypt a
compromised host, or decide how long an operator may lawfully retain personal
request evidence.
Optional account mail treats email links as bearer secrets, not sessions or MFA.
`authmail` binds random single-use digests to account identity, purpose, current
credential and expiry. Address changes additionally require current password,
session and both mailboxes. Reset requires an already verified current mailbox;
success creates no login and removes no enrolled factor. The SQLite adapter
atomically rechecks authority, revokes sessions/grants and queues notices with
the audit. Existing ownership and financial records are not reassigned.
SMTP authenticates only after verified TLS with no plaintext fallback. Encrypted
outbox payloads require an application-owned key, which may itself be wrapped in
SQLite only when its external wrapping key is kept separately. Compromise of
the sender, recipient mailbox or application host remains a threat. SMTP DATA
acceptance is not inbox delivery and crash retries are not exactly once. Forms,
CSRF/origin enforcement, fragment-to-POST handling, IP/concurrency limits and
non-enumerating responses remain explicit consumer responsibilities; see the
[integration boundaries](../authmail/README.md).
Applications must pass the authenticated user and requested resource hierarchy
to `access.Authorize`; possessing a platform-level `auth` role does not bypass
that decision. Team membership is resolved by the repository rather than
accepted from request input. Break-glass access lasts at most one hour and is
not a substitute for ordinary role policy.
Local storage adapters assume the parent directory and host account are trusted.
They reject a symlink at the configured final path and apply bounded file modes,
but they do not defend against a concurrent privileged actor replacing path
ancestors during an open. The synchronous JSONL adapter deliberately favors
durable, bounded evidence over maximum request throughput; the application owns
rotation, retention, disk monitoring, and health escalation. Its default is
mode `0600`; the sole wider option is mode `0640` for a deployment-assigned
collector group. The toolkit does not select or change that group.