🔐 Security architecture
Deliberate transparency. This page documents our defences
and their limits. No security through obscurity: a red team deserves to know what it is attacking. All code is licensed
AGPL-3.0. For the test framework, see the
rules of engagement; for demonstrations, the
Attack Simulator.
1. Authentication — SelfRecover no email
- No email, no phone number. On sign-up: you choose a recovery word → a 16-character
password and an EFF diceware passphrase are generated server-side. - Storage:
Argon2id(password), Argon2id(passphrase), Argon2id(derived_key) — m=64 MB, t=4, p=2 (OWASP profile). No secret is ever stored in the clear. - Per-domain derivation:
HMAC-SHA256(secret, domain ‖ site_salt) → a secret phished on another domain does not yield the right key. - Progressive rate limiting (5 failures / 15 min) plus a per-IP sign-up cap (anti-enumeration, anti-spam).
2. Data encryption — two models, by sensitivity
a) Server blind-key (profile: bio, location, link) — AES-256-GCM, key derived from a server secret held outside the database and outside the webroot. A SQL dump yields nothing but blobs.
b) Client-side end-to-end (personal memo) — encrypted in the browser (WebCrypto). PBKDF2 (600k) → HKDF per label → a random vault_key encrypts the memo, itself wrapped in two envelopes (password and recovery passphrase). The server holds no key.
✅ What this protects
- Blind-key: stolen disk, SQL dump, injection
- E2E: even admin or root access on the server leaves the memo unreadable
⛔ What it does not (V1, acknowledged)
- Blind-key: an admin, or an RCE that reads the key, can decrypt profiles and private messages
- E2E: a persistently compromised server serving tampered JavaScript that captures the password at unlock — the "served code" problem
3. The shared foundation — SelfRecover ⇄ SelfDataGuard
- One memorised root secret, one shared derivation primitive, and child keys separated by label (
auth / data-enc / data-recover). - Cardinal rule: never the same key for authentication and encryption. The server sees authentication; it must never be able to decrypt.
- Unified recovery: the same recovery word or passphrase restores access and data. Recovery strength comes from the entropy of the input (diceware passphrase), not from hash length.
4. Application hardening
- CSRF: session-bound HMAC token, verified on every write action (
X-CSRF-Token header). - Headers:
Content-Security-Policy, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy, HSTS. - Sessions: 192-bit random token,
HttpOnly/SameSite=Lax cookie, 24 h TTL with purge. - Anti-enumeration: non-discriminating error messages, per-IP limits.
5. Moderation — SelfModerate anti-manipulation
- Per-member reputation (starting at 20/30), ±1 votes on posts and on members.
- Anti-Sybil: an account under 24 h old with no contribution cannot vote.
- Anti pack-voting: 3 or more coordinated downvotes within 60 s are cancelled and the reputation restored.
- Anti upvote-farming: repeated mutual votes are neutralised. Graduated sanctions — loss of voting rights, then 24 h → 7 d → 30 d → permanent ban.
6. Threat model — stated honestly
✅ Mitigated
- Database exfiltration (dump, stolen disk) — blobs only
- Password brute-forcing (rate limiting)
- Moderation manipulation (Sybil, pack-voting)
- CSRF, clickjacking, passive session theft
- Memo theft, even with root access (E2E at rest)
⚠️ Known limits (V1)
- Profiles and private messages readable by an admin or an RCE (blind-key)
- Persistently compromised server → tampering with the served code
- Metadata is not encrypted (who talks to whom, and when)
7. Roadmap (beyond V1)
E2E extended to private messages and profiles · Argon2id replacing PBKDF2 · an external integrity supervisor (detecting tampered served code and abnormal behaviour, with reversible automatic containment) · distributed quorum (Shamir) for critical keys.