Skip to content

Checklist de Código Seguro

Use este checklist ao criar ou modificar endpoints, especialmente ao lidar com dados do usuário, autenticação ou integrações externas.

Autenticação e autorização

  • [ ] O endpoint precisa de autenticação? Aplicar ClerkAuthGuard (padrão global)
  • [ ] É realmente público? Só então usar @Public() — revisar se faz sentido
  • [ ] Qual permissão é necessária? Adicionar @Permissions('app:modulo')
  • [ ] A ação modifica dados? Verificar se read-only roles devem ser bloqueadas
  • [ ] Envolve escalonamento de privilégios? Validar level do usuário vs. alvo

Isolamento de tenant

  • [ ] Toda query tem organizationId no where?
  • [ ] Nunca confiar em organizationId do body — sempre usar do TenantContext
  • [ ] Recursos de outras organizações retornam 404 (não 403) para não revelar existência

Dados sensíveis

  • [ ] O endpoint recebe CPF? Nunca armazenar em texto puro — usar hashCpf()
  • [ ] O endpoint recebe senha? Usar EncryptionService.encrypt()
  • [ ] O endpoint recebe token de API externa? Usar EncryptionService.encrypt()
  • [ ] A resposta inclui campos sensíveis? Mascarar antes de retornar
  • [ ] Está logando dados de usuário? Garantir que não tem PII (CPF, email, token)

Validação de entrada

  • [ ] Inputs do usuário têm validação com class-validator?
  • [ ] Strings têm @MaxLength()?
  • [ ] Números têm @Min() e @Max()?
  • [ ] Arrays têm @ArrayMaxSize()?
  • [ ] Enums têm @IsIn()?

Webhooks externos

  • [ ] Validar assinatura HMAC? (Meta usa x-hub-signature-256, Clerk usa Svix)
  • [ ] Usar crypto.timingSafeEqual() para comparação de hashes
  • [ ] Retornar 200 imediatamente, processar assincronamente (evitar timeout)
  • [ ] Deduplicar por ID do evento (Redis com TTL)

Operações destrutivas

  • [ ] Deletar um recurso — verificar se pertence à organização do usuário?
  • [ ] Operações em bulk — limitar quantidade máxima?
  • [ ] Há rollback se falhar no meio?

Endpoints de arquivo/upload

  • [ ] Validar MIME type além da extensão
  • [ ] Limitar tamanho (ex: 5MB para imagens)
  • [ ] Não executar arquivos recebidos
  • [ ] Armazenar em MinIO, não no sistema de arquivos do container

Rate limiting

  • [ ] Endpoint pode ser abusado? Adicionar @Throttle()
  • [ ] Endpoint público sensível? Considerar rate limit mais agressivo

Auditoria

  • [ ] A ação é sensível? Adicionar regra no AuditInterceptor
  • [ ] Incluir metadados úteis (IDs relevantes, mas sem PII)

Comunicação com serviços externos

  • [ ] Timeout configurado? (HTTP timeouts no HttpModule)
  • [ ] Retry logic? (BullMQ para operações assíncronas, retry.util.ts para síncronas)
  • [ ] O que acontece se o serviço externo ficar indisponível?

Exemplos de código seguro

Query com isolamento de tenant

typescript
// ✅ Correto
const campaign = await this.prisma.campaign.findFirst({
  where: {
    id: campaignId,
    organizationId: TenantContext.organizationId, // sempre incluir
  },
});

// ❌ Errado
const campaign = await this.prisma.campaign.findUnique({
  where: { id: campaignId }, // sem tenant isolation!
});

Comparação de tokens

typescript
// ✅ Correto — timing-safe
const a = Buffer.from(receivedToken);
const b = Buffer.from(expectedToken);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
  throw new ForbiddenException();
}

// ❌ Errado — vulnerável a timing attack
if (receivedToken !== expectedToken) {
  throw new ForbiddenException();
}

Retorno de dados sensíveis

typescript
// ✅ Correto — mascara token na resposta
return {
  ...instance,
  token: '********',
  accessToken: '********',
};

// ❌ Errado — expõe token
return instance;