4ce2df0a2b
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 14s
Deploy Staging / deploy (push) Successful in 18s
CI / backend (pull_request) Successful in 52s
CI / frontend (pull_request) Successful in 15s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 55s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 54s
80 lines
4.5 KiB
Markdown
80 lines
4.5 KiB
Markdown
# 06 — Security Model (Part 5)
|
|
|
|
Builds on the existing hardening (read-only scope, encrypted tokens, IDOR global query
|
|
filters, SSRF egress guard, non-root containers, confirmed destructive actions) and adds
|
|
what multi-user + admin + multi-provider require.
|
|
|
|
## RBAC
|
|
- Roles: **Admin** · **Member** (small-team, one org). First user bootstraps as Admin.
|
|
- Enforced by **policy-based authorization** at the API (ASP.NET Core policies), not in the UI.
|
|
|
|
| Capability | Member | Admin |
|
|
|------------|:------:|:-----:|
|
|
| Read/manage **own** mail & accounts | ✅ | ✅ |
|
|
| Own user settings | ✅ | ✅ |
|
|
| Link/unlink **own** provider accounts | ✅ | ✅ |
|
|
| View/manage **other users** | ❌ | ✅ |
|
|
| Toggle **feature flags** (incl. AI global) | ❌ | ✅ |
|
|
| Enable/disable **providers** | ❌ | ✅ |
|
|
| **Maintenance mode**, system settings | ❌ | ✅ |
|
|
| View **audit log** & monitoring | ❌ | ✅ |
|
|
- **No cross-user data access, ever** — Admin manages *accounts/flags/system*, **not** other
|
|
users' email contents (privacy). Admin power is over the *platform*, not people's inboxes.
|
|
|
|
## OAuth token storage
|
|
- Refresh/access tokens **encrypted at rest** with the Data Protection API (AES); keys persist
|
|
to the mounted `/keys` volume (existing). Decrypted **only in-memory** for the moment of an
|
|
API call. **Never logged, never sent to the browser.**
|
|
- 1:1 `provider_tokens` per account; rotation timestamped; a compromised/rotated token is
|
|
replaced atomically. Token columns are `bytea` ciphertext, not readable in DB dumps.
|
|
|
|
## Session handling
|
|
- Opaque **server-side sessions** (DB-backed) + HttpOnly/Secure/SameSite cookie; **id rotated
|
|
on login** (anti-fixation); idle + absolute expiry; server-side **revocation** (logout,
|
|
sign-out-everywhere, admin revoke, role change). CSRF via SameSite + token.
|
|
|
|
## Admin access protection
|
|
- Admin routes require the **Admin policy**; sensitive mutations (toggle AI global, disable a
|
|
provider, suspend a user, enter maintenance) require a **confirmation / step-up** and are
|
|
**rate-limited**.
|
|
- **Every admin action is audit-logged** (`audit_logs`: actor, action, target, metadata, ip,
|
|
time) — append-only.
|
|
- First-admin bootstrap is one-time; afterwards admin is grant-only by an existing Admin
|
|
(logged). Guard against privilege escalation: role changes are Admin-only + audited + force
|
|
session refresh.
|
|
|
|
## API security boundaries
|
|
- **Per-user isolation** via EF **global query filters** (extended to `account_id`/`user_id`)
|
|
so a query can *never* return another user's rows — the IDOR safeguard, now multi-account.
|
|
- **Input validation** (FluentValidation) on all DTOs; **mass-assignment safe** (explicit DTOs,
|
|
no entity binding).
|
|
- **Rate limiting** on auth, admin, search, and AI endpoints.
|
|
- **SSRF egress guard** (existing) constrains all outbound calls — provider APIs, IMAP hosts,
|
|
Ollama, and any opt-in cloud AI — to an allowlist; user-supplied IMAP hosts are validated.
|
|
- **Security headers** (CSP, HSTS, X-Frame-Options, etc.) via the reverse proxy/API; strict CORS.
|
|
|
|
## Multi-provider & AI specifics
|
|
- **Least-privilege scopes** per provider; extra scopes added per-feature with consent.
|
|
- **Provider isolation:** disabling a provider flag revokes its use cleanly; per-account tokens
|
|
are independent (one reauth doesn't affect others).
|
|
- **Prompt injection:** email content is untrusted → LLM output is **advisory only, never
|
|
triggers actions**; a human/rule confirms. AI runs **local by default**; cloud AI is explicit
|
|
opt-in with per-feature consent + egress logging.
|
|
- **Attachments/vision:** sandboxed parsing, size/type limits, never executed.
|
|
|
|
## Threat model (summary)
|
|
| Threat | Mitigation |
|
|
|--------|------------|
|
|
| Account hijack via linking | Must authenticate as target user; unique `(provider, sub)`; linking an owned identity blocked |
|
|
| Token theft / DB exposure | Encryption at rest; tokens never in logs/browser; rotation |
|
|
| Privilege escalation | Admin-only role changes, audited, session refresh; policy checks server-side |
|
|
| IDOR / cross-user leakage | Global query filters on user_id/account_id |
|
|
| CSRF / session fixation | SameSite + token; session id rotation; server-side revoke |
|
|
| SSRF (providers/IMAP/AI) | Egress allowlist guard; validate user-supplied hosts |
|
|
| Prompt injection | AI advisory-only; never acts; local-first |
|
|
| Mass admin abuse | Rate limit + step-up + full audit trail |
|
|
|
|
## Non-negotiables
|
|
Admins manage the platform, **not** users' inboxes · tokens encrypted & browser-invisible ·
|
|
every privileged action audited · AI never required and never acts autonomously.
|