← Documentation · Security & data · LGPD · Measurement
Security · postureSecurity posture
Threat model and control boundaries for Compre Barato Alagoas — for reviews, scanners, and anyone assessing whether open source is compatible with production.
docs/security-posture.md.1. What is public on purpose
| Surface | Why | Primary control |
|---|---|---|
| Code on GitHub (MIT) | Transparency, LGPD, civic reuse | Secrets never in git (.env.example only) |
POST /api/v1/search and product routes | The app needs the backend | Rate limit, validation, cache, no extra PII |
docs.* site | Product and privacy | No credentials |
| Prices (SEFAZ-AL origin) | Public NFC-e data | SEFAZ token only on the server |
Typical scanner finding: “Documented API / visible endpoints.”
Response: the client already reveals the contract. Open source and product
docs do not replace authentication or limits — and are not a vulnerability by themselves
if the controls below are active.
2. What is not a security boundary
- Repository visibility — hiding the code does not remove the app's HTTPS API.
- Only hiding the admin URL — without
ADMIN_TOKENthe admin API returns 401 (fail-closed). - Swagger/ReDoc in production — makes scanning easier; that is why it stays off in production (section 4).
3. Surfaces and controls
Application API
- No account login; optional device identity (consent / cloud lists).
- Device token on the server only as a salted hash.
- Daily search limit (
DAILY_SEARCH_LIMIT). - Location used for search; no trajectory retained as a profile.
Admin API (/admin/api/*)
- Empty
ADMIN_TOKEN⇒ 401 on every admin route. - Constant-time comparison (
hmac.compare_digest). - Vhost
admin.<domain>; Fernet vault in Redis (status returns fingerprint only).
Secrets
ADMIN_TOKEN, SECRET_ENCRYPTION_KEY, SEFAZ token, Anthropic key, and DB passwords
live in the .env only on the VPS (or encrypted panel) — never in git or the client app.
API published on the host only at 127.0.0.1:8000 behind nginx.
LLM / SEFAZ
- User input treated as inert data in the prompt; deterministic fallback if the model fails.
- Limited SEFAZ fan-out; Redis cache reduces repetition.
4. OpenAPI / Swagger in production
| Environment | /docs, /redoc, /openapi.json |
|---|---|
development (local) | On |
production (deploy) | Off (404) |
EXPOSE_API_DOCS=true | On (escape hatch) |
EXPOSE_API_DOCS=false | Off |
App nginx only proxies /api and /health — it does not publish an interactive explorer at the edge.
Product-language endpoints remain under Documentation → API.
Important: turning off OpenAPI does not hide the app contract; it reduces noise in automated pentests and avoids an unnecessary exploration console on the same host as the API.
5. Frequent findings
| Finding | Severity we adopt | Treatment |
|---|---|---|
| Public / “documented” API | Informational if section 3 is OK | Keep open source; cite this doc; ensure section 4 in prod |
| OpenAPI in production | Low/medium hygiene | Off by default in production |
| Admin without authentication | Critical if token empty and sensitive data | Fail-closed; strong token on the VPS |
| Secret in the repository | Critical | Rotate; never commit .env |
| Abuse / LLM cost | Medium operational | Rate limit, cache, monitor admin |
| Prompt injection | Medium | Hardened prompt + fallback + tests |
6. Quick checklist
- With
ENVIRONMENT=production:GET /docsand/openapi.json→ 404. /admin/api/...without token → 401.- No real
.envor SEFAZ token in the public repository. - Redis/Postgres only on the compose network; API only localhost on the host.
CORS_ORIGINSrestricted in production (not*for sensitive scenarios).- Privacy aligned with the code (hashes, consent for cloud lists).
7. Why open source
- Prices are already public (SEFAZ-AL); the value is in the secure bridge and honest UX.
- LGPD claims remain auditable.
- Closing GitHub does not close the app's HTTPS API.
Restricted to the team (private repo or VPS only): operational secrets and internal notes — not this design.
8. Honest limitations
- Root on the VPS can still read memory and the on-disk
.env(HSM/KMS would be another tier). - Without Redis the API does not start (fail fast).
- This text does not replace a formal pentest or external DPO opinion.
Aligned with code: backend/app/config.py (api_docs_enabled),
backend/app/main.py, deploy/nginx/alagoas.precospublicos.ia.br.conf.