Desenvolvimento Salesforce

SOQL dinâmico sem abrir brecha de SQL injection

Ilustração do artigo

Em algum momento você precisa montar a query em runtime: filtros que o usuário escolhe, campos descobertos dinamicamente, relatórios configuráveis. O problema é que concatenar string para montar SOQL abre a porta para injection. Dá para ter o dinamismo sem o risco. Este guia mostra as quatro defesas que eu aplico.

O risco: concatenar entrada do usuário na query

O padrão perigoso é este. O valor vem do cliente e entra direto na string da query:

// NÃO faça isso
String q = 'SELECT Id FROM Account WHERE Name = \'' + entrada + '\'';
Database.query(q); // entrada = "x' OR Name != '" quebra a intenção da query

Defesa 1: valores sempre por bind variables

Nunca coloque o valor na string. Passe por bind, com Database.queryWithBinds. O valor é tratado como dado, nunca como parte do código da query.

Map<String, Object> binds = new Map<String, Object>{ 'nome' => entrada };
Database.queryWithBinds(
    'SELECT Id FROM Account WHERE Name = :nome',
    binds, AccessLevel.USER_MODE);

Defesa 2: nomes de campo só entram se estiverem numa whitelist

Bind resolve valores, mas não resolve nome de campo ou de objeto vindos do cliente (para montar filtros dinâmicos, por exemplo). A regra: o nome só entra na query se existir numa whitelist gerada pelo próprio schema, via Schema.describe. Nome desconhecido lança exceção, nunca vai para a query.

Map<String, Schema.SObjectField> campos =
    Account.SObjectType.getDescribe().fields.getMap();
if (!campos.containsKey(campoDoCliente.toLowerCase())) {
    throw new AuraHandledException('Campo inválido');
}
// só agora campoDoCliente pode entrar na string da query

Defesa 3: literais que não aceitam bind passam por escape

Alguns trechos (como valores dentro de INCLUDES em multipicklist) não aceitam bind. Nesses casos, aplique String.escapeSingleQuotes no literal antes de concatenar.

String literal = String.escapeSingleQuotes(valor);

Defesa 4: a query final roda em USER_MODE

Rodar com WITH USER_MODE (ou AccessLevel.USER_MODE) faz o Salesforce respeitar o CRUD e o FLS do usuário de ponta a ponta. Mesmo que a lógica esteja correta, o usuário nunca vê campo ou registro que ele não teria permissão de ver. Esse mesmo modo cobre os três níveis de acesso que todo código Apex precisa respeitar, não só na query dinâmica. Segurança em profundidade. Na Summer '26 esse USER_MODE vira o padrão do Apex na API 67, então forçá-lo hoje é se adiantar ao que a plataforma já está tornando obrigatório.

O teste que prova que está seguro

Toda camada de SOQL dinâmico deveria ter um teste que tenta injetar e verifica que a tentativa é rejeitada. Se um nome de campo malicioso como Name FROM Account-- for aceito, o teste falha. É assim que você garante que a whitelist realmente protege.

Precisa de filtros dinâmicos ou relatórios configuráveis com segurança?

Eu construo camadas de consulta dinâmica à prova de injection, com describe, bind e USER_MODE, tudo testado inclusive contra tentativa de injection. Comece com um diagnóstico gratuito de 45 minutos.

Falar no WhatsApp Ver serviços