Security hardening
FestIn finds exposure — don't let the dashboard become one.
Threat model
| Asset | Threat |
|---|---|
| Dashboard (findings, secrets in redacted matches) | unauthorized access, token theft |
| JWT secret | forgery of admin tokens |
/auth/login | brute force (no rate limit in-app) |
| Scanner | scanning targets you don't own (legal exposure) |
| SQLite file | contains all findings — treat as sensitive |
Mandatory checklist
-
Set
FESTIN_JWT_SECRETto a random value. The dev fallback is public knowledge; with it, anyone can mint admin tokens: -
Delete the demo database (
rm data/festin.db) — the checked-in one has known credentials (admin/admin123). - Strong bootstrap password. The first registered user becomes admin; register it yourself before exposing the service.
- TLS everywhere. Terminate at your proxy/ingress; the service speaks plain HTTP (docker).
-
Second-layer rate limit at the proxy. Login/register are limited in-app (5 attempts / 60 s per IP, env-tunable — see configuration); a proxy limit is still worth it as defense in depth:
-
Bind to
127.0.0.1or a private network when not behind a proxy.0.0.0.0only inside containers. - Treat findings as secrets. The API redacts matches (
AKIA****EXAMPLE) but rule names, buckets and object paths leak structure. Limit dashboard access like you limit your SIEM. - Back up with
.backup, notcp(docker).
Dependabot alert: python-ecdsa (Minerva timing attack)
ecdsa is a transitive dependency of python-jose (JWT support). FestIn only uses HS256 (symmetric HMAC) — module-level import tracing confirms ecdsa is never imported at runtime, so the P-256 timing attack does not apply. The alert is dismissed with this justification. If you ever switch to asymmetric algorithms (ES256), replace python-jose with PyJWT and re-evaluate.
Auth model — know its edges
| Fact | Consequence |
|---|---|
| JWT HS256, 60 min, no refresh/revocation | a stolen token is valid ≤ 60 min; shortening expiry = more re-logins |
Token lives in localStorage | XSS in any injected script = token theft; don't inject scripts |
| No account lockout | proxy rate-limit is the mitigation |
| Viewer role is read-only by API and UI | mutations are admin-guarded server-side (_admin_guard) |
| Bootstrap: first register on empty DB = admin | expose the service publicly after bootstrapping, or keep an allowlist until then |
Scanner legality
FestIn probes third-party infrastructure (DNS, HTTP, cloud storage endpoints).
- Only scan assets you own or are authorized to test (bug-bounty scope, contracts).
--toranonymizes probes but doesn't make unauthorized scanning legal.- Respect rate limits:
--profile stealthexists for a reason; a--concurrency 50run looks like an attack.
Runtime isolation
# container with no network surprises: read-only root, drop capabilities
docker run -d --name festin \
--read-only --tmpfs /tmp \
--cap-drop ALL --security-opt no-new-privileges \
-p 8420:8420 -v festin-data:/data \
-e FESTIN_JWT_SECRET=$SECRET \
festin:local
In Kubernetes: the manifests on the Kubernetes page already set runAsNonRoot; add a NetworkPolicy if the dashboard should not reach arbitrary targets (it must reach DNS + HTTP + cloud storage endpoints for scanning).
Reporting a vulnerability
Open a private advisory via GitHub Security Advisories — not a public issue.