From 7903e454dc369b0e6d255ce3bdc16a78b6f8f165 Mon Sep 17 00:00:00 2001 From: Kyle Pope Date: Tue, 17 Mar 2026 01:43:39 +0800 Subject: [PATCH] Strip detailed security internals from README Reduces the security section to a brief summary without exposing specific middleware names, rate limit thresholds, lockout parameters, or implementation details that could aid threat actors. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index ce54b39..1bda640 100644 --- a/README.md +++ b/README.md @@ -110,29 +110,7 @@ A self-hosted, multi-user life administration app with a dark-themed UI and role ## Security -### Hardened by default - -- **Multi-user data isolation** — all resources scoped by `user_id` with per-query filtering; pentest-verified (51+ test cases, 0 exploitable IDOR findings) -- **Role-based access control** — `admin` and `standard` roles with `require_admin` dependency on all admin endpoints -- **CSRF protection** — global `CSRFHeaderMiddleware` requires `X-Requested-With: XMLHttpRequest` on all mutating requests -- **Input validation** — `extra="forbid"` on all Pydantic schemas prevents mass-assignment; `max_length` on all string fields; `ge=1, le=2147483647` on path IDs -- **Non-root containers** — both backend (`appuser:1000`) and frontend (`nginx-unprivileged`) run as non-root -- **No external backend port** — port 8000 is internal-only; all traffic flows through nginx -- **Server version suppression** — `server_tokens off` (nginx) and `--no-server-header` (uvicorn) -- **Rate limiting** — nginx `limit_req_zone` (10 req/min) on `/api/auth/login` (burst=5), `/verify-password` (burst=5), `/change-password` (burst=5), `/totp-verify` (burst=5), `/setup` (burst=3) -- **DB-backed account lockout** — 10 failed attempts triggers 30-minute lock per account -- **Inactive user blocking** — disabled accounts rejected at login (HTTP 403) without session creation, lockout reset, or last_login_at update -- **Timing-safe login** — dummy Argon2id hash for non-existent users prevents username enumeration -- **Password reuse prevention** — change-password endpoint rejects same password as old -- **Dotfile blocking** — `/.env`, `/.git/config`, etc. return 404 (`.well-known` preserved for ACME) -- **CSP headers** — Content-Security-Policy on all responses, scoped for Google Fonts -- **CORS** — configurable origins with explicit method/header allowlists -- **API docs disabled in production** — Swagger/ReDoc/OpenAPI only available when `ENVIRONMENT=development` -- **Argon2id password hashing** with transparent bcrypt migration on first login -- **DB-backed sessions** — revocable, with signed itsdangerous httpOnly cookies, 7-day sliding window with 30-day hard ceiling -- **Optional TOTP MFA** — authenticator app support with backup codes, admin-enforced MFA for new users -- **Invited editor field allowlist** — can_modify invitees restricted to content fields only (title, description, time, color, location); calendar moves, recurring series changes, and deletions blocked server-side -- **5 penetration tests passed** — RBAC, header hardening, SSRF, shared calendars, event invitations; 0 exploitable findings +UMBRA is hardened by default with multi-user data isolation, role-based access control, CSRF protection, non-root containers, rate limiting, account lockout, optional TOTP MFA, and secure session management. Multiple penetration tests have been conducted with no exploitable findings. ### Production Hardening @@ -141,21 +119,15 @@ Before deploying to production, generate secure values for your `.env`: ```bash # Generate a secure SECRET_KEY (64-char hex string) python3 -c "import secrets; print(secrets.token_hex(32))" -# or: openssl rand -hex 32 # Generate a secure database password python3 -c "import secrets; print(secrets.token_urlsafe(24))" -# or: openssl rand -base64 24 - -# Set ENVIRONMENT to disable Swagger/ReDoc and auto-enable secure cookies -ENVIRONMENT=production ``` Additionally for production: +- Set `ENVIRONMENT=production` — disables API docs and auto-enables HTTPS-only session cookies - Place behind a reverse proxy with TLS termination (e.g., Caddy, Traefik, or nginx with Let's Encrypt) -- Set `ENVIRONMENT=production` — this disables API docs and auto-enables HTTPS-only session cookies (`COOKIE_SECURE` derives from `ENVIRONMENT`; override with `COOKIE_SECURE=false` if running non-TLS prod behind a proxy) - Set `CORS_ORIGINS` to your actual domain (e.g., `https://umbra.example.com`) -- Consider adding HSTS headers at the TLS-terminating proxy layer ## API Overview