Skip to main content

Requisitos — [Back][#07]Correção SQL Injection no header api-company-target

Task #198276

Task pai: User Story 198232 ADRs relacionados:Contexto técnico: plano · resumo · grill-decisions

Status: refinado Sessão de grilling: decisões consolidadas em grill-decisions (2026-07-08)


Visão geral

Corrigir a vulnerabilidade crítica de SQL Injection no header HTTP api-company-target, que hoje é lido sem validação e interpolado como identificador de schema PostgreSQL em queries Dapper e EF Core. A correção centraliza a validação em UserProvider.GetSchemaName() na biblioteca Coezzion.Common (coezzion-nuget-common) e elimina os 22 pontos de leitura direta do header que contornam o IUserProvider. Chamadas legítimas com api-company-target: arezzo continuam funcionando sem alteração no app Flutter nem no BFF.


Papéis

PapelResponsabilidade
Biblioteca Coezzion.CommonValidar o schema resolvido via header api-company-target antes de retorná-lo aos consumidores
Serviços backend (cart, checkout, product, store, backoffice, integration, message, payments-reports, showcase, jobs)Consumir schema exclusivamente via IUserProvider.GetSchemaName(); migrar leituras diretas do header
BFFRepassar o header api-company-target para downstream sem validação adicional nesta task
App FlutterContinuar enviando api-company-target: arezzo em requisições autenticadas
Atacante externoVetor de ataque: envio de payloads SQL no header api-company-target

Requisitos

RF-01 — Validação centralizada do schema no UserProvider

User Story: Como equipe de segurança, eu quero que o schema resolvido via header api-company-target seja validado em um único ponto da biblioteca compartilhada para que todos os serviços que usam IUserProvider.GetSchemaName() fiquem protegidos com uma única alteração.

Acceptance Criteria:

  1. WHEN uma requisição HTTP contém o header api-company-target com valor arezzo THEN o UserProvider.GetSchemaName() SHALL retornar "arezzo".
  2. WHEN o header api-company-target está ausente ou vazio e nenhuma fonte de maior prioridade resolve o schema THEN o UserProvider.GetSchemaName() SHALL seguir o fluxo existente de fallback (SchemaNameScopedWrapper.SchemaName) sem alteração de comportamento.
  3. WHEN o header x-api-key é igual a venda_ai THEN o UserProvider.GetSchemaName() SHALL retornar "arezzo" hardcoded, sem passar pela validação do header api-company-target.
  4. WHEN o JWT contém claim schema com valor preenchido THEN o UserProvider.GetSchemaName() SHALL retornar o valor da claim sem validação adicional nesta task (escopo do card #30).
  5. WHEN qualquer serviço que já usa IUserProvider.GetSchemaName() recebe requisição com header api-company-target malicioso THEN o schema inválido SHALL ser barrado antes de chegar a queries Dapper ou ao MultiTenantContextProvider do EF Core.

RF-02 — Barreira de regex para formato de identificador SQL

User Story: Como desenvolvedor backend, eu quero que o schema passe por validação de formato antes da whitelist para bloquear caracteres perigosos de injeção SQL.

Acceptance Criteria:

  1. WHEN o valor do header api-company-target não corresponde ao padrão regex ^[a-z][a-z0-9_]*$ THEN o UserProvider.GetSchemaName() SHALL rejeitar o schema.
  2. WHEN o header contém aspas (", '), ponto-e-vírgula (;), hífen (-), espaços ou letras maiúsculas THEN o UserProvider.GetSchemaName() SHALL rejeitar o schema via regex.
  3. WHEN o header contém comentário SQL (ex.: --) THEN o UserProvider.GetSchemaName() SHALL rejeitar o schema via regex.
  4. WHEN o valor do header possui mais de 63 caracteres THEN o UserProvider.GetSchemaName() SHALL rejeitar o schema.
  5. WHEN o valor do header é nulo, vazio ou apenas espaços em branco THEN o UserProvider.GetSchemaName() SHALL rejeitar o schema.

RF-03 — Whitelist de schemas autorizados

User Story: Como equipe de produto, eu quero que apenas organizações explicitamente autorizadas possam ter seu schema resolvido para impedir acesso a schemas de outros tenants.

Acceptance Criteria:

  1. WHEN o valor do header api-company-target passa na validação de regex mas não está na whitelist THEN o UserProvider.GetSchemaName() SHALL rejeitar o schema.
  2. WHEN o valor do header é brizza ou qualquer schema válido por regex que não esteja na whitelist THEN o UserProvider.GetSchemaName() SHALL rejeitar o schema.
  3. WHEN o valor do header é arezzo e passa na validação de regex THEN o UserProvider.GetSchemaName() SHALL aceitar o schema.
  4. WHEN novas organizações forem adicionadas no futuro THEN a expansão da whitelist SHALL ser tratada em evolução separada (fora desta task).

RF-04 — Rejeição de schema inválido sem vazamento de informação

User Story: Como equipe de segurança, eu quero que tentativas de injeção SQL via header resultem em falha sem expor ao atacante qual barreira bloqueou o payload.

Acceptance Criteria:

  1. WHEN o schema resolvido via header api-company-target falha em qualquer barreira de validação (regex, tamanho ou whitelist) THEN o UserProvider.GetSchemaName() SHALL lançar InvalidOperationException.
  2. WHEN um schema inválido é rejeitado THEN a resposta HTTP resultante SHALL ser status 500 (erro interno), sem distinguir para o cliente se o motivo foi schema inválido ou outro erro interno.
  3. WHEN um schema inválido é rejeitado THEN o sistema SHALL registrar o erro em log/APM para investigação interna.
  4. WHEN ErrorHandling__IncludeErrorDetail está desabilitado em staging e produção THEN mensagens de erro do PostgreSQL com nomes de schema ou tabela SHALL NOT ser expostas ao cliente.

RF-05 — Eliminação de bypass por leitura direta do header

User Story: Como desenvolvedor backend, eu quero que nenhum ponto do código leia o header api-company-target diretamente de IHttpContextAccessor para que a validação central não possa ser contornada.

Acceptance Criteria:

  1. WHEN um serviço precisa resolver o schema de tenant THEN o serviço SHALL obter o valor exclusivamente via IUserProvider.GetSchemaName().
  2. WHEN DatabaseConfig.GetSchema() em product, store, backoffice, integration, message, payments-reports ou showcase lê o header diretamente THEN o código SHALL ser alterado para usar IUserProvider.GetSchemaName().
  3. WHEN event handlers de background em payments-reports ou integration leem o header diretamente THEN o código SHALL ser alterado para injetar IUserProvider e chamar GetSchemaName().
  4. WHEN repositórios em showcase ou payments-reports capturam o schema no construtor via leitura direta do header THEN o código SHALL ser alterado para injetar IUserProvider.
  5. WHEN o endpoint gRPC ProductAppServiceapi-company-target dos headers gRPC THEN o código SHALL ser alterado para resolver o schema via IUserProvider (ou mecanismo que popula SchemaNameScopedWrapper com schema validado).
  6. WHEN a migração dos 22 pontos de leitura direta é concluída THEN nenhum arquivo nos serviços afetados SHALL conter leitura direta de Request.Headers["api-company-target"].

RF-06 — Proteção automática de cache e queries existentes

User Story: Como desenvolvedor backend, eu quero que decorators de cache e queries Dapper com interpolação de schema existentes fiquem protegidos sem refatoração massiva.

Acceptance Criteria:

  1. WHEN um decorator de cache chama _userProvider.GetSchemaName() para compor a chave de cache THEN a chave SHALL usar apenas schema validado após o fix central.
  2. WHEN uma query Dapper usa interpolação de string com o schema (ex.: $"SELECT * FROM \"{schema}\".\"Carts\"") e o schema veio de IUserProvider.GetSchemaName() THEN a query SHALL ser considerada segura sem refatoração para parametrização de identificador.
  3. WHEN o MultiTenantContextProvider recebe o schema de IUserProvider.GetSchemaName() THEN o search_path do PostgreSQL SHALL usar apenas schema validado.

RF-07 — Testes unitários de validação de schema

User Story: Como desenvolvedor backend, eu quero testes automatizados que comprovem que payloads de SQL injection são bloqueados e que o uso legítimo continua funcionando.

Acceptance Criteria:

  1. WHEN o header é arezzo THEN o teste SHALL confirmar retorno de "arezzo".
  2. WHEN o header contém aspas, ponto-e-vírgula, comentário SQL (--), espaço, hífen ou letras maiúsculas THEN o teste SHALL confirmar lançamento de InvalidOperationException.
  3. WHEN o header está vazio THEN o teste SHALL confirmar lançamento de InvalidOperationException.
  4. WHEN o header possui 64 ou mais caracteres THEN o teste SHALL confirmar lançamento de InvalidOperationException.
  5. WHEN o header é brizza (schema válido por regex mas fora da whitelist) THEN o teste SHALL confirmar lançamento de InvalidOperationException.
  6. WHEN um endpoint de serviço consumidor recebe header malicioso (ex.: arezzo"; DROP TABLE "Carts"; --) THEN o teste de integração SHALL confirmar resposta HTTP 500.

Fora de Escopo

  • Card #30 — header api-company-target sobrescreve claim schema do JWT (task 198279)
  • Card #08 — vazamento cross-tenant de perfis de fraude (task 198277)
  • Validação de schema em webhooks de pagamento (Braspag/PagarMe via MerchantOrderId)
  • Validação adicional de schema no BFF antes de repassar o header
  • Whitelist dinâmica via GetAllOrganizationSchemas() do banco Organizations
  • Refatoração arquitetural de multi-tenancy (connection string por tenant)
  • Rate limit ou WAF para bloqueio de payloads de injeção
  • Parametrização de identificadores SQL em todas as ~200 queries Dapper existentes
  • Alteração no comportamento do app Flutter ou do envio do header pelo cliente

Dependências

DependênciaDescriçãoStatus
coezzion-nuget-commonBiblioteca Coezzion.Common com UserProvider e IUserProviderExistente — alteração necessária
Serviços consumidoresAtualização da versão da nuget após publicação do fixPendente
coezzion-service-authNão lê api-company-target — não afetadoN/A
Card #30 (task 198279)Validação do claim JWT schema — decisão separadaFora do escopo desta task
ErrorHandling__IncludeErrorDetail=falseMitigação profunda contra vazamento de estrutura do banco em errosVerificar em staging/prod