# 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= → 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: ` 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= 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.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.