9.6 KiB
Security and privacy
Baseline
Design and test against OWASP ASVS Level 2, OWASP MASVS, and the OWASP API Security Top 10. Perform a documented threat-model review before the first public production release. Security must remain proportionate and lightweight, but simplicity never justifies weakening isolation or cryptography.
Threat model
Protect against credential stuffing, phishing, token theft/replay, broken object authorization, malicious uploads, injection, CSRF, XSS, compromised clients, accidental logging, enumeration, insecure backups, reference-update tampering, and cross-user data leakage. A fully compromised host administrator or rooted phone can defeat application protections; the product must disclose these boundaries.
Transport and browser security
- Production accepts only an HTTPS public URL and secure effective requests.
- Require TLS 1.2+, recommend TLS 1.3 and HSTS at the reverse proxy.
- Trust
Forwarded/X-Forwarded-*only from configured proxy peers. - Web sessions use
Secure,HttpOnly, and appropriateSameSitecookies. - Protect state-changing cookie requests with CSRF tokens and origin validation.
- Apply strict CSP, frame denial,
nosniff, restrictive referrer policy, and private/no-store caching where sensitive. - CORS allows only configured origins and never combines wildcard origins with credentials.
- Secrets and tokens never appear in URLs.
- Certificate pinning is not mandatory because instances use arbitrary domains and certificate authorities.
Authentication
Passwords are hashed with a current, parameterized password-hashing function such as Argon2id. Password rules favor length, breached-password screening when available without leaking the password, and rate-limited verification over arbitrary composition rules.
Password login requires TOTP. Setup presents both an otpauth QR code and the textual secret for compatible managers, plus single-use recovery codes. TOTP secrets are encrypted at rest. Recovery codes are hashed.
Passkeys use WebAuthn/FIDO2, require origin/RP-ID correctness, user verification where available, and support multiple credentials. Passkey login replaces password plus TOTP; sensitive account changes still require recent authentication.
Authentication and recovery responses must resist account enumeration. Login, TOTP, passkey, invitation, recovery, and pairing endpoints use layered rate limits by IP, account, device, and instance with bounded progressive delay.
Mobile device trust
Each Companion installation generates a non-exportable signing key in Android Keystore where supported. Pairing registers its public key and a human-readable device entry. QR bootstrap secrets are random, account-bound, single-use, and expire within 5-10 minutes.
Access tokens are short-lived and audience-bound. Refresh tokens are opaque, high-entropy, stored securely, rotated on every use, bound to the device/session, and support reuse detection that revokes the token family. Sensitive sync or account operations may require a signed envelope containing method, path, body digest, timestamp, and nonce. The server enforces a short clock window and one-time nonce.
Users can list, rename, and revoke their own devices and sessions and disconnect all others. Administrators can revoke compromised access but cannot use the mechanism to impersonate users.
Root/bootloader-compromise detection is best-effort and never blocks use. Show a dismissible warning explaining that privileged software may bypass local protections.
Authorization and tenant isolation
Every object has an immutable server-controlled owner. The server derives owner scope from the authenticated principal and ignores/rejects client owner assignment except explicit administrative metadata operations. Use opaque identifiers, but never rely on their unpredictability.
Tests MUST prove that user A cannot read, infer existence, modify, delete, export, share, or fetch media belonging to B. Administrators have operational permissions, not health-data access. Use indistinguishable 404/403 behavior where it reduces enumeration without harming legitimate diagnostics.
Sharing is explicit, granular by selected item/dataset, visible, revocable, and never implied by household membership or administrator status.
Application encryption
Sensitive PocketBase fields and all private media are encrypted by the Go server before storage. Use a well-reviewed authenticated-encryption construction such as XChaCha20-Poly1305 or AES-256-GCM with unique nonces. Bind ciphertext to stable context using additional authenticated data: record type, record ID, owner ID, schema/key version.
/config/master.key is a cryptographically random master key generated during initial setup. Derive purpose-separated subkeys with HKDF for records, photos, thumbnails, avatars, settings/secrets, and backup metadata. The key is never stored in PocketBase, logs, images, or environment variables.
The setup completion page and later security settings allow a strongly reauthenticated administrator to download a key copy. Every key download is audited. Loss of the key makes encrypted data unrecoverable.
Voluntary key change
An administrator may explicitly press Change encryption key. Require recent password+TOTP or passkey authentication and a recent verified encrypted backup. Explain that all protected records and media will be decrypted and re-encrypted and service may enter maintenance mode.
The operation generates a new key, writes a durable migration journal, processes bounded batches, verifies every new ciphertext, and preserves the old key until all records, files, and metadata validate. Writes are blocked or safely dual-handled during the operation. Only after a complete verification is the new key atomically installed and the old key securely removed. On failure, rollback leaves the old key and original data usable. A crash must resume or rollback deterministically.
Mobile storage
- Store device keys and session secrets using Android Keystore-backed secure storage.
- Encrypt the local SQLite database with a maintained solution.
- Disable unencrypted Android backup for sensitive files or define encrypted backup rules explicitly.
- Never place sensitive data in ordinary preferences, clipboard, notifications, logs, analytics, or crash attachments.
- Allow biometric/local-code app locking and optional screenshot/recent-screen protection.
- Remove temporary plaintext images promptly.
Upload and media security
Treat uploads as hostile. Enforce authenticated ownership, byte limits before decoding, pixel limits, time/memory bounds, magic-byte and decode validation, supported-format allowlists, re-encoding, EXIF removal, random storage identifiers, quotas, and rate limits. Reject malformed, polyglot, oversized, decompression-bomb, or unexpected files. Media is delivered only through authorized API routes with private/no-store cache behavior and never through enumerable static paths.
Input and API hardening
- Strict request schemas; reject unknown fields where compatibility does not require them.
- Prepared SQL for
references.db; no client-provided SQL or raw PocketBase filters. - Mandatory bounded pagination and limited filter/sort vocabularies.
- Bounded JSON nesting, batch sizes, decompression, query time, and response size.
- Idempotency keys for retriable mutations and sync batches.
- Generic production errors with request IDs; details remain in sanitized internal logs.
Audit
Audit security events, not health content: successful/failed login, TOTP/passkey use, recovery, email/password changes, device/session creation and revocation, passkey/TOTP changes, invitation, role change, export, account deletion, backup/restore, master-key download/change, reference update, and sensitive configuration changes.
An audit entry includes UTC time, normalized event type/result/reason, opaque account/device IDs, request ID, source IP, and simplified user agent. It never contains credentials, token material, OTP values, health values, notes, photos, or request bodies.
Users see their own security history. Administrators see instance security events without health data. Default retention is six months, configurable from 1-12 months, with clear privacy disclosure and automatic purge. Do not geolocate IP addresses automatically.
Backups
Global backups are created by administrators; personal exports are created by users. A global .hbackup archive is encrypted with a user-entered strong password. Derive an archive key using Argon2id with random salt and stored parameters, then encrypt/authenticate the entire logical archive with a modern AEAD. Filenames and sensitive manifest data are inside the encrypted envelope. The password and server master key are not included.
Backups contain consistent PocketBase data, encrypted/private media in portable logical form, configuration required for restore, schema/version metadata, checksums, and audit data according to policy. references.db may be omitted because it is redistributable. Restore validates format, authenticity, checksums, compatibility, quotas, and ownership before committing. Import on an instance with a different master key decrypts the archive and encrypts data under that instance key.
Backups must be restorable in automated tests. Explain that deleted data may persist until encrypted-backup retention expires.
Supply chain and operations
Lock dependency versions, scan source/dependencies/images/secrets, publish an SBOM, sign release images, avoid floating latest in production examples, and keep release builds reproducible where practical. Security-sensitive authentication, authorization, crypto, upload, and backup changes require focused review and negative tests.