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
| Papel | Responsabilidade |
|---|---|
Biblioteca Coezzion.Common | Validar 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 |
| BFF | Repassar o header api-company-target para downstream sem validação adicional nesta task |
| App Flutter | Continuar enviando api-company-target: arezzo em requisições autenticadas |
| Atacante externo | Vetor 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:
- WHEN uma requisição HTTP contém o header
api-company-targetcom valorarezzoTHEN oUserProvider.GetSchemaName()SHALL retornar"arezzo". - WHEN o header
api-company-targetestá ausente ou vazio e nenhuma fonte de maior prioridade resolve o schema THEN oUserProvider.GetSchemaName()SHALL seguir o fluxo existente de fallback (SchemaNameScopedWrapper.SchemaName) sem alteração de comportamento. - WHEN o header
x-api-keyé igual avenda_aiTHEN oUserProvider.GetSchemaName()SHALL retornar"arezzo"hardcoded, sem passar pela validação do headerapi-company-target. - WHEN o JWT contém claim
schemacom valor preenchido THEN oUserProvider.GetSchemaName()SHALL retornar o valor da claim sem validação adicional nesta task (escopo do card #30). - WHEN qualquer serviço que já usa
IUserProvider.GetSchemaName()recebe requisição com headerapi-company-targetmalicioso THEN o schema inválido SHALL ser barrado antes de chegar a queries Dapper ou aoMultiTenantContextProviderdo 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:
- WHEN o valor do header
api-company-targetnão corresponde ao padrão regex^[a-z][a-z0-9_]*$THEN oUserProvider.GetSchemaName()SHALL rejeitar o schema. - WHEN o header contém aspas (
",'), ponto-e-vírgula (;), hífen (-), espaços ou letras maiúsculas THEN oUserProvider.GetSchemaName()SHALL rejeitar o schema via regex. - WHEN o header contém comentário SQL (ex.:
--) THEN oUserProvider.GetSchemaName()SHALL rejeitar o schema via regex. - WHEN o valor do header possui mais de 63 caracteres THEN o
UserProvider.GetSchemaName()SHALL rejeitar o schema. - 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:
- WHEN o valor do header
api-company-targetpassa na validação de regex mas não está na whitelist THEN oUserProvider.GetSchemaName()SHALL rejeitar o schema. - WHEN o valor do header é
brizzaou qualquer schema válido por regex que não esteja na whitelist THEN oUserProvider.GetSchemaName()SHALL rejeitar o schema. - WHEN o valor do header é
arezzoe passa na validação de regex THEN oUserProvider.GetSchemaName()SHALL aceitar o schema. - 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:
- WHEN o schema resolvido via header
api-company-targetfalha em qualquer barreira de validação (regex, tamanho ou whitelist) THEN oUserProvider.GetSchemaName()SHALL lançarInvalidOperationException. - 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.
- WHEN um schema inválido é rejeitado THEN o sistema SHALL registrar o erro em log/APM para investigação interna.
- WHEN
ErrorHandling__IncludeErrorDetailestá 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:
- WHEN um serviço precisa resolver o schema de tenant THEN o serviço SHALL obter o valor exclusivamente via
IUserProvider.GetSchemaName(). - 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 usarIUserProvider.GetSchemaName(). - WHEN event handlers de background em payments-reports ou integration leem o header diretamente THEN o código SHALL ser alterado para injetar
IUserProvidere chamarGetSchemaName(). - 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. - WHEN o endpoint gRPC
ProductAppServicelêapi-company-targetdos headers gRPC THEN o código SHALL ser alterado para resolver o schema viaIUserProvider(ou mecanismo que populaSchemaNameScopedWrappercom schema validado). - 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:
- 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. - WHEN uma query Dapper usa interpolação de string com o schema (ex.:
$"SELECT * FROM \"{schema}\".\"Carts\"") e o schema veio deIUserProvider.GetSchemaName()THEN a query SHALL ser considerada segura sem refatoração para parametrização de identificador. - WHEN o
MultiTenantContextProviderrecebe o schema deIUserProvider.GetSchemaName()THEN osearch_pathdo 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:
- WHEN o header é
arezzoTHEN o teste SHALL confirmar retorno de"arezzo". - 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 deInvalidOperationException. - WHEN o header está vazio THEN o teste SHALL confirmar lançamento de
InvalidOperationException. - WHEN o header possui 64 ou mais caracteres THEN o teste SHALL confirmar lançamento de
InvalidOperationException. - WHEN o header é
brizza(schema válido por regex mas fora da whitelist) THEN o teste SHALL confirmar lançamento deInvalidOperationException. - 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-targetsobrescreve claimschemado 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 bancoOrganizations - 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ência | Descrição | Status |
|---|---|---|
coezzion-nuget-common | Biblioteca Coezzion.Common com UserProvider e IUserProvider | Existente — alteração necessária |
| Serviços consumidores | Atualização da versão da nuget após publicação do fix | Pendente |
coezzion-service-auth | Não lê api-company-target — não afetado | N/A |
| Card #30 (task 198279) | Validação do claim JWT schema — decisão separada | Fora do escopo desta task |
ErrorHandling__IncludeErrorDetail=false | Mitigação profunda contra vazamento de estrutura do banco em erros | Verificar em staging/prod |