Ilustração de uma seringa (injeção) tentando perfurar uma barreira de código seguro

Evitando SQL injection: as 5 práticas essenciais para devs WordPress

Em 2026, os ataques de SQL injection continuam sendo uma das principais causas de invasão em sites WordPress. E o motivo quase sempre é o mesmo: código mal escrito que permite a um atacante manipular consultas ao banco de dados. Se você desenvolve plugins, temas ou qualquer funcionalidade personalizada, conhecer e aplicar as práticas corretas para evitar SQL injection não é opcional – é obrigação. Este guia mostra as 5 práticas essenciais que todo dev WordPress deve dominar.

⚠️ O risco é real: Em 2025, 34% das vulnerabilidades reportadas no repositório oficial de plugins WordPress envolviam SQL injection. Na maioria dos casos, a correção era simples: usar prepared statements.

O que é SQL injection (na prática)

Imagine que você tem uma consulta que busca um usuário pelo ID passado na URL:

$user_id = $_GET['id'];
$query = "SELECT * FROM wp_users WHERE ID = $user_id";
$results = $wpdb->get_results($query);

Um atacante pode enviar algo como: ?id=1 OR 1=1; DROP TABLE wp_users; --. O resultado: a query se torna SELECT * FROM wp_users WHERE ID = 1 OR 1=1; DROP TABLE wp_users; --todos os usuários são retornados e a tabela é deletada. Simples, devastador, e infelizmente comum.

As 5 práticas essenciais

1️⃣ Use $wpdb->prepare() para prepared statements

A função prepare() da classe $wpdb é a primeira linha de defesa. Ela escapa automaticamente os valores, garantindo que qualquer dado fornecido pelo usuário seja tratado como valor, não como parte da sintaxe SQL.

// ❌ ERRADO
$user_id = $_GET['id'];
$query = "SELECT * FROM wp_users WHERE ID = $user_id";
$user = $wpdb->get_row($query);

// ✅ CORRETO
$user_id = absint($_GET['id']);
$query = $wpdb->prepare("SELECT * FROM wp_users WHERE ID = %d", $user_id);
$user = $wpdb->get_row($query);

Placeholders: %s para string, %d para inteiro, %f para float. Nunca concatene variáveis diretamente na string SQL.

2️⃣ Use funções específicas do $wpdb (get_row, get_var, etc.) quando possível

Para tarefas comuns (buscar um campo, contar registros), o $wpdb já oferece métodos que internamente tratam os dados. Além disso, sempre que possível, evite escrever SQL manual e use as funções de alto nível (ex: WP_Query, get_metadata, get_terms).

// ✅ Recomendado (abstração segura)
$user = get_user_by('id', $user_id);

// ✅ Alternativa com $wpdb (ainda segura)
$user = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->users WHERE ID = %d", $user_id));
Comparação visual: código vulnerável concatenando vs código seguro com prepared statement
3️⃣ Sanitize e valide TODOS os inputs externos

Antes mesmo de construir a query, certifique-se de que o dado está no formato esperado. Use funções do WordPress:

  • absint() – converte para inteiro positivo
  • sanitize_text_field() – limpa strings básicas
  • sanitize_email() – valida e-mail
  • sanitize_key() – para slugs e chaves alfanuméricas
  • esc_sql() – escape para SQL (mas prefira prepare)
$user_id = isset($_GET['id']) ? absint($_GET['id']) : 0;
if ($user_id === 0) {
    wp_die('ID inválido');
}

Nunca confie em dados vindos de $_GET, $_POST, $_COOKIE, $_SERVER ou até do banco de dados (se o banco já foi comprometido).

4️⃣ Use capacidades e nonces para proteger ações específicas

SQL injection muitas vezes é combinado com falhas de autorização. Mesmo que um atacante consiga injetar SQL, se a rota que ele está atacando exigir uma capacidade específica (ex: manage_options) ou um nonce válido, o dano pode ser mitigado.

// Verifica se o usuário atual pode editar posts
if (!current_user_can('edit_posts')) {
    wp_die('Acesso negado');
}

// Verifica nonce para ações via POST
if (!wp_verify_nonce($_POST['nonce'], 'meu_plugin_action')) {
    wp_die('Nonce inválido');
}

Isso não substitui a sanitização, mas adiciona uma camada extra de segurança.

5️⃣ Para campos LIKE, use esc_like() e prepare() corretamente

Consultas com LIKE são particularmente problemáticas porque caracteres curinga (% e _) precisam ser escapados. O WordPress oferece esc_like() para isso.

$search = '%' . $wpdb->esc_like($user_input) . '%';
$query = $wpdb->prepare("SELECT * FROM $wpdb->posts WHERE post_title LIKE %s", $search);
$results = $wpdb->get_results($query);

Não tente escapar manualmente com addslashes ou str_replace – sempre use as funções nativas.

📌 Lembrete importante: A função esc_sql() existe, mas é considerada obsoleta para prepared statements. Use $wpdb->prepare() sempre que possível. esc_sql() só deve ser usado em casos muito específicos (ex: nomes de tabelas dinâmicos).

Exemplo prático: formulário de busca vulnerável vs. seguro

Código vulnerável (NUNCA use):

$search = $_POST['search'];
$query = "SELECT * FROM wp_posts WHERE post_title LIKE '%$search%'";
$results = $wpdb->get_results($query);

Código seguro (use assim):

$search = sanitize_text_field($_POST['search']);
$search_like = '%' . $wpdb->esc_like($search) . '%';
$query = $wpdb->prepare("SELECT * FROM $wpdb->posts WHERE post_title LIKE %s", $search_like);
$results = $wpdb->get_results($query);
Fluxo de um formulário de busca com validação, sanitização e prepared statement

Ferramentas para detectar SQL injection no seu código

  • Query Monitor – mostra todas as queries executadas. Verifique se alguma está concatenando variáveis diretamente.
  • PHPStan com extensão WordPress – análise estática que detecta uso incorreto de $wpdb.
  • Semgrep – regras personalizadas para buscar padrões de concatenação em SQL.
  • WordPress Coding Standards (PHPCS) – as regras WordPress.DB.PreparedSQL e WordPress.DB.DirectDatabaseQuery apontam más práticas.

E se você está usando WP_Query ou funções de alto nível?

Ótimo! Funções como WP_Query, get_posts, get_metadata, get_terms já sanitizam os parâmetros internamente. Por exemplo:

$args = [
    's' => $_GET['s'],        // busca por texto livre
    'post_type' => 'post',
];
$query = new WP_Query($args);

Mesmo assim, sanitize os parâmetros antes de passar para o array, especialmente 's' (busca) e 'meta_query' valores.

🔒 Dica bônus: Sempre use $wpdb->prefix ao invés de wp_ fixo no nome das tabelas. Permite que o site use prefixos personalizados e evita SQL injection na parte do nome da tabela.

O que fazer se seu site foi atacado?

  1. Isole o site (modo manutenção ou bloqueio por IP).
  2. Restaure backup limpo (anterior ao ataque).
  3. Verifique logs de acesso e de erros (seu servidor e o debug.log do WordPress).
  4. Instale um firewall de aplicação (WAF) temporariamente.
  5. Audite todos os plugins e temas customizados em busca de SQL injection (use as ferramentas citadas).
  6. Altere todas as senhas (admin, FTP, banco de dados).
  7. Após corrigir, remova o WAF e reative o site.

Em caso de invasão confirmada, considere contratar um especialista em segurança para limpeza profunda.

Ilustração de desenvolvedor fazendo auditoria de código em busca de SQL injection com lupa

Conclusão: segurança é processo, não produto

Aplicar as 5 práticas acima reduz drasticamente o risco de SQL injection no WordPress. Mas lembre-se: a segurança nunca é definitiva. Mantenha-se atualizado sobre novas vulnerabilidades, use plugins de segurança (ex: Wordfence, Sucuri) e sempre revise seu código ou o de terceiros.

Resumo rápido:

  • ✅ Sempre use $wpdb->prepare() com placeholders.
  • ✅ Sanitize e valide todos os inputs externos.
  • ✅ Para consultas LIKE, use esc_like() antes de prepare().
  • ✅ Prefira funções de alto nível (WP_Query, get_user_by).
  • ✅ Adicione nonces e verificações de capacidade.

🔒 Quer mais segurança para seus projetos?

Na nossa loja você encontra plugins de firewall, scanners de vulnerabilidade e temas com código auditado. Não deixe a segurança do seu site para depois.

🛒 Explorar catálogo de segurança →

Ainda com dúvidas sobre como proteger seu código? Continue nos acompanhando! Publicaremos mais conteúdo sobre segurança no WordPress.


Você já sofreu ou encontrou SQL injection em algum projeto? Compartilhe sua experiência com outros profissionais e vamos fortalecer a segurança da comunidade WordPress.

Para utilizar nosso site, é necessário concordar com nossos termos de consentimento, adesão e suporte. Por isso, recomendamos que você leia-os atentamente antes de prosseguir.