413 lines
14 KiB
Markdown
413 lines
14 KiB
Markdown
# Feature Specification: Cibersegurança do Wizard — Telemetria em Tempo Real (021)
|
||
|
||
**Criado:** 2026-06-17
|
||
**Solicitado por:** Roger
|
||
**Status:** Spec — implementação pendente
|
||
**Prioridade:** P1
|
||
**Sistema:** Wizard VM112 (`/opt/ligbox-wizard`) + Portal onboard + Desk VM122
|
||
**Módulo Desk:** `wizard-security` (novo — Spec 015)
|
||
**Depende de:** Spec 001/014 (webhooks + funil), Spec 015 (módulos), Spec 016 (handoff + sessionStorage)
|
||
**UI principal:** **Audit Overview** → tenant VM112 + **Infra 2** + **Eventos**
|
||
|
||
---
|
||
|
||
## Resumo
|
||
|
||
Estender a observabilidade do onboarding com **vias de cibersegurança** dos processos do wizard: CSP (browser), auditoria de inputs (API VM112), integridade do handoff server-side e eventos de abuso — tudo visível **em tempo quase real** no Desk VM122, na mesma sessão (`session_id` / hash) do funil.
|
||
|
||
**Princípio:** isto é **camada extra de detecção e resposta** — complementa (não substitui) HTTPS, handoff one-time, React escaping e ausência de SQL no fluxo de credenciais.
|
||
|
||
**Regra de ouro:** **nunca** enviar senhas, tokens de handoff completos nem corpos de request com PII sensível nos webhooks de segurança.
|
||
|
||
---
|
||
|
||
## Problema
|
||
|
||
| Hoje | Necessidade |
|
||
|------|-------------|
|
||
| Desk vê **progresso** do funil (`session.started` → `completed`) | Ver também **tentativas de abuso** na mesma sessão |
|
||
| Audit Overview = saúde infra (Carbonio, DNS, cert) | Card **Segurança onboarding** por tenant VM112 |
|
||
| CSP / validação de inputs inexistentes ou invisíveis | Política activa + relatórios no Desk |
|
||
| IA/fuzzing acelera ataques | Alertas operacionais sem esperar abuse report |
|
||
|
||
---
|
||
|
||
## Modelo de ameaças (wizard VM112)
|
||
|
||
| Ameaça | Camada actual | Gap | Via Spec 021 |
|
||
|--------|---------------|-----|--------------|
|
||
| **XSS** (ler sessionStorage) | React escape + sem `dangerouslySetInnerHTML` | Sem CSP nem reporte | CSP + `security.csp_violation` |
|
||
| **SQL injection** | Handoff sem SQL (JSON encriptado) | Outras APIs futuras | Middleware `security.input_blocked` |
|
||
| **Roubo handoff token** | Token opaco, TTL 15 min, one-shot | Sem telemetria de reutilização | `security.handoff_rejected` |
|
||
| **Senha na URL** | Nunca — só `?onboard_handoff=` | — | Auditoria confirma ausência |
|
||
| **MITM** | HTTPS Let's Encrypt | — | Fora de escopo (já coberto) |
|
||
| **Fuzzing / brute** | Parcial | Sem rate limit visível | `security.rate_limited` |
|
||
| **Path traversal / SSRF** | Não auditado | — | `security.input_blocked` |
|
||
| **IA a gerar payloads** | Mesmas defesas | Sem feed SOC | Eventos no Desk em segundos |
|
||
|
||
### O que sessionStorage **não** é
|
||
|
||
- **Não** protege contra SQL injection (corre só no browser).
|
||
- **Risco real:** XSS bem-sucedido → script lê `ligbox_onboard_password`.
|
||
- **Resposta Spec 021:** CSP reduz superfície + reporta violações; inputs maliciosos bloqueados antes de chegar ao estado.
|
||
|
||
---
|
||
|
||
## Vias de processo — mapa completo
|
||
|
||
### Via 1 — Credenciais (Portal → Wizard)
|
||
|
||
```
|
||
Utilizador (onboard.ligbox.com.br)
|
||
→ Self-Service / Login (HTTPS)
|
||
→ POST /onboard-handoff [VM112 API — senha encriptada server-side]
|
||
→ redirect ?onboard_handoff=<token_opaco>
|
||
→ POST /consume [one-shot, apaga token]
|
||
→ sessionStorage.ligbox_onboard_password [temporário, mesma origem]
|
||
→ wizard /onboard
|
||
```
|
||
|
||
**Eventos de segurança:**
|
||
|
||
| Evento | Quando |
|
||
|--------|--------|
|
||
| `security.handoff_created` | Handoff emitido (sem senha no payload) |
|
||
| `security.handoff_consumed` | Consume OK |
|
||
| `security.handoff_rejected` | Token expirado, reutilizado, session mismatch |
|
||
| `security.handoff_expired` | TTL 15 min excedido |
|
||
|
||
### Via 2 — Inputs do wizard (passos 0–N)
|
||
|
||
Rotas VM112 a auditar (mínimo):
|
||
|
||
| Endpoint / acção | Campos |
|
||
|------------------|--------|
|
||
| Validação domínio | `domain`, FQDN |
|
||
| Conta admin | `localPart`, `domain`, `email` |
|
||
| DNS / Cloudflare | `domain`, records |
|
||
| Portal users | `login_id`, `planned_corporate_email` |
|
||
| Company profile | `legal_name`, `tax_id`, texto livre |
|
||
| Handoff consume | `token`, `session_id` |
|
||
|
||
**Eventos:**
|
||
|
||
| Evento | Severidade | Acção API |
|
||
|--------|------------|-----------|
|
||
| `security.input_warn` | baixa | Sanitizar + log + webhook |
|
||
| `security.input_blocked` | alta | HTTP 400 + log + webhook |
|
||
| `security.rate_limited` | média | HTTP 429 + webhook |
|
||
|
||
**Padrões detectados (regex/heurística MVP):**
|
||
|
||
- SQLi: `' OR `, `UNION SELECT`, `; DROP`, `1=1--`
|
||
- XSS: `<script`, `javascript:`, `onerror=`, `onload=`
|
||
- Path: `../`, `%2e%2e`
|
||
- Command: `` ` ``, `$(`, `|`, `&&`
|
||
- Oversize: campo > limite (ex. domínio > 253, nome > 500)
|
||
|
||
**Nunca logar:** `password`, `root_password`, corpo de `/consume` com segredos.
|
||
|
||
### Via 3 — CSP (browser → Desk)
|
||
|
||
```
|
||
Browser (portal + wizard)
|
||
→ viola Content-Security-Policy
|
||
→ POST report-uri / report-to
|
||
→ VM122 /api/v1/security/csp-report
|
||
→ webhook_events (source: vm112-security)
|
||
→ Audit Overview + Infra 2 + Eventos
|
||
```
|
||
|
||
**Header CSP (Traefik / nginx — portal + wizard):**
|
||
|
||
```
|
||
Content-Security-Policy:
|
||
default-src 'self';
|
||
script-src 'self';
|
||
style-src 'self' 'unsafe-inline';
|
||
img-src 'self' data: https:;
|
||
connect-src 'self' https://desk.ligbox.com.br;
|
||
frame-ancestors 'none';
|
||
base-uri 'self';
|
||
report-uri https://desk.ligbox.com.br/api/v1/security/csp-report;
|
||
```
|
||
|
||
(Ajustar `connect-src` para APIs VM112/Traefik em produção.)
|
||
|
||
**Evento:** `security.csp_violation` — inclui `blocked-uri`, `violated-directive`, `document-uri` (sem dados de utilizador).
|
||
|
||
### Via 4 — Correlação com funil (VM122)
|
||
|
||
Cada evento `security.*` **deve** incluir quando disponível:
|
||
|
||
- `session_id` (hash UUID)
|
||
- `domain`
|
||
- `client_ip` (ingress)
|
||
- `endpoint` / `wizard_step`
|
||
- `severity`: `info` | `warn` | `high` | `critical`
|
||
|
||
O Desk correlaciona com timeline Spec 014 na mesma sessão.
|
||
|
||
---
|
||
|
||
## Módulo Desk (Spec 015)
|
||
|
||
| Campo | Valor |
|
||
|-------|--------|
|
||
| `id` | `wizard-security` |
|
||
| `label` | Segurança Wizard |
|
||
| `default_enabled` | `true` |
|
||
| `nav_views` | _(enrichment — Audit Overview, Infra 2, Eventos)_ |
|
||
|
||
Desactivar módulo → APIs devolvem payload sem `security_summary`; UI oculta card e filtro.
|
||
|
||
---
|
||
|
||
## RBAC
|
||
|
||
| Acção | Perfis |
|
||
|-------|--------|
|
||
| Ver card Segurança no Audit Overview | `super_admin`, `ops_lead` |
|
||
| Ver feed segurança Infra 2 | `super_admin`, `ops_lead`, `noc` |
|
||
| Ver eventos `security.*` em Eventos | `super_admin`, `ops_lead`, `noc` |
|
||
| POST `csp-report` | Público (browser) — rate limit + validação schema |
|
||
| POST webhook segurança VM112 | `X-Webhook-Secret` (mesmo ou derivado de onboard) |
|
||
|
||
Técnicos `technician` — sem card segurança (opcional: só tickets ligados).
|
||
|
||
---
|
||
|
||
## API Desk (VM122)
|
||
|
||
### Ingestão
|
||
|
||
| Método | Path | Auth | Descrição |
|
||
|--------|------|------|-----------|
|
||
| POST | `/api/v1/security/csp-report` | nenhum (browser) | Relatório CSP (JSON W3C ou legacy) |
|
||
| POST | `/api/v1/webhooks/security` | `X-Webhook-Secret` | Eventos VM112 `security.*` |
|
||
| POST | `/api/v1/webhooks/onboard` | existente | Aceitar também `security.*` no mesmo ingress (opcional) |
|
||
|
||
### Consulta
|
||
|
||
| Método | Path | Descrição |
|
||
|--------|------|-----------|
|
||
| GET | `/api/v1/security/events?window=24h&session_id=` | Lista eventos segurança |
|
||
| GET | `/api/v1/security/summary?tenant_id=1` | KPIs para Audit Overview |
|
||
| GET | `/api/v1/audit/tenants/1/details` | Enriquecido com `security_summary` + `security_events_recent` |
|
||
|
||
### Payload webhook (VM112 → VM122)
|
||
|
||
```json
|
||
{
|
||
"event": "security.input_blocked",
|
||
"session_id": "ee2239fd-dd05-444e-b79c-a5701a255ba8",
|
||
"domain": "evil.example",
|
||
"data": {
|
||
"endpoint": "POST /api/domains/validate",
|
||
"field": "domain",
|
||
"reason": "sql_injection_pattern",
|
||
"pattern_id": "sqli_union",
|
||
"client_ip": "203.0.113.42",
|
||
"wizard_step": 0,
|
||
"severity": "high"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Proibido no payload:** passwords, tokens handoff completos, headers `Authorization`.
|
||
|
||
### Persistência
|
||
|
||
Tabela nova ou reutilizar `webhook_events`:
|
||
|
||
| Opção | Prós |
|
||
|-------|------|
|
||
| **A** — `webhook_events` com `source=vm112-security` | Reutiliza Eventos, Infra 2, funil |
|
||
| **B** — tabela `security_events` dedicada | Queries mais rápidas, retenção própria |
|
||
|
||
**MVP:** opção A (consistente com arquitectura actual).
|
||
|
||
Índices recomendados: `(source, created_at)`, `(session_id)`, `(event_type)`.
|
||
|
||
---
|
||
|
||
## API VM112 (wizard)
|
||
|
||
### Middleware `security_audit.py` (novo)
|
||
|
||
- Executar **antes** do handler em rotas listadas na Via 2.
|
||
- Retornar 400/429 com corpo genérico (não revelar qual regex matched em produção — opcional `reason` interno no webhook only).
|
||
- Fire-and-forget POST para VM122 (não bloquear UX se Desk offline).
|
||
|
||
### Cliente webhook
|
||
|
||
Reutilizar cliente existente de onboarding (`session.started`, etc.) com fila retry (3 tentativas, backoff 2s).
|
||
|
||
### Config (.env VM112)
|
||
|
||
```
|
||
DESK_SECURITY_WEBHOOK_URL=https://desk.ligbox.com.br/api/v1/webhooks/security
|
||
DESK_WEBHOOK_SECRET=<mesmo WEBHOOK_SECRET>
|
||
SECURITY_AUDIT_ENABLED=true
|
||
SECURITY_RATE_LIMIT_PER_IP=60/min
|
||
```
|
||
|
||
---
|
||
|
||
## UI Desk
|
||
|
||
### Audit Overview → modal VM112 Ligbox Onboard
|
||
|
||
Novo bloco **«Segurança onboarding»** (acima ou abaixo do resumo domínios):
|
||
|
||
| KPI | Exemplo |
|
||
|-----|---------|
|
||
| Violações CSP (24h) | 3 |
|
||
| Inputs bloqueados | 1 |
|
||
| Handoffs rejeitados | 0 |
|
||
| Sessões com alerta | 2 |
|
||
|
||
Lista recente (clicável → detalhe):
|
||
|
||
| Hora | Sessão (hash) | Evento | IP | Domínio |
|
||
|------|---------------|--------|-----|---------|
|
||
| 21:42 | `ee2239fd…` | `security.csp_violation` | 203.0.113.1 | — |
|
||
| 21:40 | `3dfa8c6c…` | `security.input_blocked` | 198.51.100.5 | `foo';DROP--` |
|
||
|
||
Clique → modal com timeline **funil + segurança** intercalados (ou abas).
|
||
|
||
### Infra 2 (SOC)
|
||
|
||
- Painel **«Segurança wizard»** no feed (15s refresh).
|
||
- Flash visual em `security.input_blocked` e `security.csp_violation` (como eventos novos no feed VM112).
|
||
|
||
### Eventos
|
||
|
||
- Filtro toolbar: **Segurança** (`source=vm112-security` ou `event` prefix `security.`).
|
||
- Colunas: severidade, evento, sessão (hash completo), domínio, IP, hora.
|
||
|
||
### Tickets (opcional Fase C)
|
||
|
||
Auto-ticket quando:
|
||
|
||
- ≥3 `security.input_blocked` mesmo IP em 10 min, ou
|
||
- `security.csp_violation` + `security.input_blocked` mesma sessão
|
||
|
||
Subject: `[security] {domain|sem domínio} — {event}` · prioridade alta.
|
||
|
||
---
|
||
|
||
## Taxonomia de eventos `security.*`
|
||
|
||
| event | Label UI | Severidade default |
|
||
|-------|----------|-------------------|
|
||
| `security.csp_violation` | Violação CSP | warn |
|
||
| `security.input_warn` | Input suspeito (sanitizado) | info |
|
||
| `security.input_blocked` | Input bloqueado | high |
|
||
| `security.rate_limited` | Rate limit | warn |
|
||
| `security.handoff_created` | Handoff criado | info |
|
||
| `security.handoff_consumed` | Handoff consumido | info |
|
||
| `security.handoff_rejected` | Handoff rejeitado | high |
|
||
| `security.handoff_expired` | Handoff expirado | info |
|
||
| `security.auth_failed` | Autenticação portal falhou (agregado) | warn |
|
||
| `security.session_anomaly` | Sessão inconsistente (IDs mismatch) | high |
|
||
|
||
---
|
||
|
||
## Fases de implementação
|
||
|
||
### Fase A — Ingestão VM122 (Desk)
|
||
|
||
- [ ] T001 `POST /api/v1/security/csp-report` + validação schema + rate limit
|
||
- [ ] T002 `POST /api/v1/webhooks/security` (ou extensão onboard ingress)
|
||
- [ ] T003 Persistência `webhook_events` source `vm112-security`
|
||
- [ ] T004 `GET /api/v1/security/summary` e `/security/events`
|
||
- [ ] T005 Registar módulo `wizard-security` em `registry.py`
|
||
|
||
### Fase B — VM112 wizard
|
||
|
||
- [ ] T006 Middleware auditoria inputs (Via 2)
|
||
- [ ] T007 Eventos handoff (Via 1)
|
||
- [ ] T008 Cliente webhook segurança + retry
|
||
- [ ] T009 Testes unitários padrões SQLi/XSS
|
||
|
||
### Fase C — UI Desk
|
||
|
||
- [ ] T010 Card Segurança no Audit Overview modal VM112
|
||
- [ ] T011 Filtro Eventos «Segurança»
|
||
- [ ] T012 Painel Infra 2 + flash eventos novos
|
||
- [ ] T013 Hash sessão na lista (já feito — correlacionar)
|
||
|
||
### Fase D — Infra Traefik/nginx
|
||
|
||
- [ ] T014 CSP headers portal + wizard (CT114 Traefik)
|
||
- [ ] T015 Validar `report-uri` reachability desde browser público
|
||
- [ ] T016 Documentar excepções CSP se libs externas exigirem
|
||
|
||
### Fase E — Resposta operacional (opcional)
|
||
|
||
- [ ] T017 Auto-ticket regras abuso
|
||
- [ ] T018 Push ntfy em `security.input_blocked` critical
|
||
- [ ] T019 Retenção 90 dias + purge `security.*` antigos
|
||
|
||
---
|
||
|
||
## Critérios de aceitação
|
||
|
||
1. Browser com CSP activo envia violação → evento visível em Eventos em <30s.
|
||
2. POST domínio `foo<script>alert(1)</script>.com` → 400 VM112 + `security.input_blocked` no Desk.
|
||
3. Reutilizar token handoff → `security.handoff_rejected` correlacionado à `session_id`.
|
||
4. Audit Overview VM112 mostra KPIs segurança 24h sem regressão nos domínios existentes.
|
||
5. Módulo `wizard-security` OFF → card oculto; APIs sem `security_summary`.
|
||
6. Nenhum webhook contém senha nem token handoff completo (auditoria manual + teste).
|
||
7. Mesma sessão: funil (`session.started`) + evento segurança partilham `session_id` no detalhe.
|
||
|
||
---
|
||
|
||
## Testes
|
||
|
||
```bash
|
||
# CSP report (simulado)
|
||
curl -s -X POST https://desk.ligbox.com.br/api/v1/security/csp-report \
|
||
-H 'Content-Type: application/csp-report' \
|
||
-d '{"csp-report":{"document-uri":"https://onboard.ligbox.com.br/onboard","violated-directive":"script-src","blocked-uri":"https://evil.com/x.js"}}'
|
||
|
||
# Input blocked (após Fase B)
|
||
curl -s -X POST https://onboard.ligbox.com.br/api/.../validate \
|
||
-d '{"domain":"x\"; DROP TABLE--"}'
|
||
# → 400 + evento no Desk
|
||
|
||
# Summary Desk
|
||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||
"https://desk.ligbox.com.br/api/v1/security/summary?tenant_id=1"
|
||
```
|
||
|
||
---
|
||
|
||
## Fora de escopo (v1)
|
||
|
||
- WAF comercial (Cloudflare WAF rules — futuro)
|
||
- SIEM externo (export syslog)
|
||
- Pentest automatizado no wizard
|
||
- Substituir sessionStorage por Web Crypto / Credential Management API
|
||
- Segurança Carbonio pós-provisionamento (Spec separada)
|
||
|
||
---
|
||
|
||
## Relação com specs existentes
|
||
|
||
| Spec | Relação |
|
||
|------|---------|
|
||
| **016** | Handoff + sessionStorage — Via 1 auditada, senha nunca no webhook |
|
||
| **014** | Relógio por fase — mesma sessão, timeline paralela segurança |
|
||
| **015** | Módulo `wizard-security` |
|
||
| **017** | Purge — independente; não apagar `security.*` antes de retenção |
|
||
| **010** | Assist/takeover — técnico vê alertas segurança na sessão assistida |
|
||
|
||
---
|
||
|
||
## Conclusão
|
||
|
||
A Spec 021 define as **vias de cibersegurança** dos processos do wizard (credenciais, inputs, CSP, handoff) com **telemetria em tempo real no VM122** — Audit Overview, Infra 2 e Eventos — sem confundir protecção (HTTPS, handoff, React) com **visibilidade operacional** (o que o Roger precisa para operar e reagir).
|
||
|
||
**Próximo passo:** Fase A (ingestão VM122) — pode começar sem alterações no wizard; Fase B liga a detecção activa na VM112.
|