Skip to content

WordPress Plugin Security Checklist — MIDDAG Account

Checklist focado em seguranca para o plugin middag-account. Claude Code: validar TODOS os itens antes de marcar codigo security-sensitive como completo.

Companion Rules

RuleFoco
wp-plugin-best-practices.mdChecklist de qualidade (15 secoes)
wp-plugin-patterns.mdPadroes de arquitetura
wp-plugin-testing-strategy.mdEstrategia de testes (incluindo seguranca)
wp-plugin-workflow.mdCiclo de desenvolvimento

1. Input Validation & Sanitization

REST API Parameters

  • [ ] sanitize_text_field() em todas entradas texto
  • [ ] sanitize_email() em campos de email
  • [ ] sanitize_textarea_field() em texto multi-linha
  • [ ] absint() em IDs numericos e contadores
  • [ ] sanitize_url() / esc_url_raw() em URLs
  • [ ] wp_kses_post() em conteudo HTML (descricoes, notas)
  • [ ] array_map('sanitize_text_field', $array) em arrays de input
  • [ ] Type casting: (int), (float), (bool) em parametros tipados
  • [ ] Enum validation: verificar valor esta na lista permitida antes de usar

Form Submissions (Admin/Frontend)

  • [ ] wp_verify_nonce() em todo form handler
  • [ ] wp_nonce_field() em todo form output
  • [ ] check_admin_referer() em form submissions admin
  • [ ] check_ajax_referer() em AJAX handlers

2. Output Escaping

HTML Context

  • [ ] esc_html() em toda saida texto em HTML
  • [ ] esc_attr() em valores de atributos HTML
  • [ ] esc_url() em atributos href/src
  • [ ] esc_textarea() em conteudo de textarea
  • [ ] wp_kses() em HTML submetido pelo usuario

JavaScript Context

  • [ ] esc_js() em strings JS inline
  • [ ] wp_json_encode() para dados passados ao JS
  • [ ] wp_add_inline_script() para dados PHP-to-JS (Inertia props)

SQL Context

  • [ ] $wpdb->prepare() em TODA SQL raw (sem excecoes)
  • [ ] Nunca concatenar input do usuario em strings SQL
  • [ ] Usar %d, %s, %f placeholders
  • [ ] Preferir WP query APIs: WP_Query, get_posts(), get_post_meta()

3. Authorization & Multi-Tenant Isolation

REST API

  • [ ] Toda rota tem permission_callback (nunca __return_true para write ops)
  • [ ] current_user_can() checks por capability, nao role
  • [ ] Object-level permissions: verificar usuario pode acessar recurso especifico
  • [ ] Rate limiting em endpoints publicos

Organization-Scoped Permissions

  • [ ] Header X-Middag-Organization validado em toda request API
  • [ ] Usuario pertence a Organization requisitada (verificar Collaborator membership)
  • [ ] Collaborator role (admin, member, viewer) verificado antes da acao
  • [ ] Dados de uma Organization NUNCA vazam para outra (hard boundary)
  • [ ] Queries filtram por organization_id em TODA consulta
  • [ ] Nenhum endpoint retorna dados cross-Organization sem permissao explicita

Admin Pages

  • [ ] current_user_can('manage_options') na registration de admin pages
  • [ ] Meta box access gated por capability relevante
  • [ ] Bulk actions verificam capability por item

Frontend Actions (Portal NextJS)

  • [ ] Toda acao via API autenticada (JWT ou nonce)
  • [ ] Organization boundary enforced no backend, nao confiar no frontend
  • [ ] Token expiry e rotation implementados

4. JWT RS256 Authentication

Token Issuance

  • [ ] RSA key pair (RS256) — nunca HS256 com shared secret
  • [ ] Private key armazenada em wp-content/ fora do webroot ou em variavel de ambiente
  • [ ] Private key com permissoes restritas (0600 ou equivalente)
  • [ ] Public key disponivel para validacao por servicos externos
  • [ ] Token payload inclui: sub (user_id), iss (site_url), iat, exp, org (organization_id)
  • [ ] Token lifetime curto (15-30 minutos)

Token Refresh

  • [ ] Refresh token com lifetime maior (7-30 dias)
  • [ ] Refresh token armazenado server-side (nao apenas client)
  • [ ] Refresh token revogavel (blacklist ou whitelist)
  • [ ] Rotacao de refresh token a cada uso (single-use)
  • [ ] Refresh endpoint nao aceita access token expirado ha mais de X horas

Token Validation

  • [ ] Verificar assinatura RS256 em toda request
  • [ ] Verificar exp (expirado = rejeitar)
  • [ ] Verificar iss (issuer = site_url)
  • [ ] Verificar sub (user exists e esta ativo)
  • [ ] Verificar org (usuario pertence a Organization)
  • [ ] Constant-time comparison para tokens
  • [ ] Rejeitar tokens com claims invalidos ou inesperados

Key Management

  • [ ] Rotacao de RSA keys suportada (key versioning)
  • [ ] Keys anteriores aceitas por periodo de graca
  • [ ] Alerta/log quando key rotation necessaria
  • [ ] Nunca commitar private keys no repositorio

5. Data Protection

Sensitive Data

  • [ ] Nenhuma credencial no source code
  • [ ] Nenhuma API key no client-side JS
  • [ ] PII do cliente acessivel apenas a usuarios autorizados
  • [ ] Emails nao expostos em REST responses para usuarios nao-autorizados
  • [ ] Organization data isolada (multi-tenant hard boundary)

File Operations

  • [ ] wp_upload_dir() para paths de upload (nunca hardcode)
  • [ ] wp_check_filetype() em arquivos uploaded
  • [ ] Sem eval(), create_function(), ou preg_replace() com modifier e
  • [ ] Sem extract() em templates — usar $args['key'] explicito
  • [ ] realpath() validation em file paths para prevenir traversal

API Keys & Secrets

  • [ ] HubSpot API key em wp_options (encrypted ou obfuscated)
  • [ ] Stripe API keys em wp_options (separadas live/test)
  • [ ] Banco Inter certificados em diretorio protegido
  • [ ] Cloudflare API token em wp_options
  • [ ] Nenhum secret logado em plain text

6. CSRF Protection

  • [ ] Todas operacoes state-changing requerem nonce
  • [ ] Nonce lifetime apropriado (default 24h geralmente OK)
  • [ ] Nonce action names especificos: middag_approve_{type}_{id}, nao generico
  • [ ] Admin AJAX: check_ajax_referer() com action especifica

7. WordPress-Specific Security

Custom Post Types

  • [ ] Nunca expor CPT data via WP REST API padrao (disable show_in_rest se usando API custom)
  • [ ] Meta keys com prefixo _middag_ para proteger de exposicao acidental
  • [ ] register_meta() com auth_callback para meta fields sensiveis
  • [ ] map_meta_cap => true para controle granular por CPT

Post Meta Security

  • [ ] Nunca confiar em meta values sem sanitizacao ao ler
  • [ ] update_post_meta() com valores sanitizados
  • [ ] delete_post_meta() com capability check antes
  • [ ] Meta queries nao expoe dados cross-Organization

Settings

  • [ ] Settings access gated por manage_options capability
  • [ ] Post-save validation para enum/range values
  • [ ] Secrets armazenados com wp_options autoload=false

8. Webhook Signature Validation

HubSpot Webhooks

  • [ ] Validar X-HubSpot-Signature ou X-HubSpot-Signature-v3 header
  • [ ] Signature calculada com app secret + request body
  • [ ] Rejeitar requests sem signature valida (HTTP 401)
  • [ ] Timestamp validation para prevenir replay attacks
  • [ ] Log webhook rejections para auditoria

Stripe Webhooks

  • [ ] Validar Stripe-Signature header com webhook_secret
  • [ ] Usar \Stripe\Webhook::constructEvent() para verificacao
  • [ ] Rejeitar eventos com signature invalida
  • [ ] Timestamp tolerance configuravel (default 300s)
  • [ ] Webhook secret diferente por conta (live vs test)
  • [ ] Idempotency: verificar event.id nao processado anteriormente

Banco Inter Webhooks

  • [ ] Validar assinatura do callback com certificado
  • [ ] Rejeitar callbacks sem autenticacao valida
  • [ ] Log todas operacoes financeiras

Regras Gerais de Webhook

  • [ ] Endpoint aceita apenas POST
  • [ ] Content-Type validation (application/json)
  • [ ] Body size limit para prevenir abuse
  • [ ] Rate limiting no endpoint de webhook
  • [ ] Retornar 200 rapidamente, processar async se necessario

9. Error Handling

  • [ ] Sem stack traces expostos ao usuario (verificar WP_DEBUG)
  • [ ] error_log() para erros internos, admin notices para user-facing
  • [ ] REST API retorna HTTP status codes apropriados, nao 200 com body de erro
  • [ ] Nunca expor estrutura do banco em mensagens de erro
  • [ ] Catch exceptions no boundary — nao deixar bubble ate WP
  • [ ] JWT errors retornam 401 com mensagem generica (nao revelar motivo exato)

10. Third-Party Dependencies

  • [ ] Composer packages auditados: composer audit
  • [ ] npm packages auditados: npm audit
  • [ ] Lock files commitados (composer.lock, package-lock.json)
  • [ ] Dependencies pinned a major versions especificas
  • [ ] Chatwoot PHP SDK (ramiroestrella/chatwoot-php-sdk) auditado

11. Distribution Security

  • [ ] Sem arquivos .env na distribuicao
  • [ ] Sem configs debug/dev no ZIP
  • [ ] Sem credenciais ou fixtures de teste na distribuicao
  • [ ] .distignore exclui: .claude/, .aiox-core/, tests/, docs/, .git/, migration/
  • [ ] Verificar conteudo do ZIP antes do release
  • [ ] RSA private keys NUNCA incluidas no ZIP

12. Multi-Tenant Data Isolation

Organization Boundary (Critical)

  • [ ] Todo dado pertence a uma Organization (foreign key obrigatorio)
  • [ ] Queries SEMPRE filtram por organization_id do usuario logado
  • [ ] REST API middleware valida Organization antes do controller
  • [ ] Nenhum endpoint permite organization_id como parametro do cliente (apenas header/token)
  • [ ] Testes de penetracao para IDOR (Insecure Direct Object Reference)
  • [ ] Admin (superadmin) pode acessar cross-Organization com audit log

Collaborator Permissions

  • [ ] Roles: admin, member, viewer — verificados em toda acao
  • [ ] Admin pode convidar/remover collaborators
  • [ ] Member tem acesso read/write limitado
  • [ ] Viewer tem acesso read-only
  • [ ] Permissoes verificadas no backend (nunca confiar no frontend)

Severity Levels

LevelRespostaExemplo
CRITICALBloquear release. Corrigir imediatamente.SQL injection, auth bypass, XSS admin, JWT leak, cross-org data leak
HIGHCorrigir antes do proximo release.Missing nonce, capability check insuficiente, webhook sem validacao
MEDIUMCorrigir no sprint.Missing output escaping em campo low-risk, token lifetime longo
LOWRastrear e corrigir.Informational disclosure, verbose errors

Automated Checks

bash
# Static analysis identifica type errors e alguns problemas de seguranca
composer check:stan

# Code style garante padroes consistentes
composer check:style

# Triggers para revisao manual:
# - Novo REST endpoint adicionado
# - Novo form handler adicionado
# - Nova database query adicionada
# - Novo file upload handler adicionado
# - Novo webhook handler adicionado
# - Novo JWT claim adicionado
# - Alteracao em Organization boundary logic
# - Nova integracao externa adicionada

MIDDAG Account WordPress Plugin Security Checklist v1.0 — Morgan (@pm), Abril 2026