SQL injection em PHP: o que é e como se proteger de uma vez por todas

Rocketseat

Rocketseat

8 min de leitura
php
Vamos imaginar o seguinte cenário: você, dev empolgado(a), acabou de criar a sua primeira aplicação PHP conectada a um banco de dados. Tudo funcionando – inserir usuário, fazer login, buscar dados. Então um colega curioso resolve testar algo inesperado no campo de login: em vez de um nome, ele digita um comando esquisito, algo como admin' OR '1'='1. De repente, boom! Ele consegue acesso sem precisar de senha. O que aconteceu? Você acaba de presenciar um ataque de SQL injection.
Para muitos iniciantes em PHP, é comum focar em aprender o básico – variáveis, funções, resolver problemas clássicos de lógica, estruturas condicionais, loops. Isso tudo é importante, mas há outra peça fundamental na jornada de um dev profissional: segurança em PHP. E dentre as vulnerabilidades mais críticas está o SQL injection. Nesta leitura, feita especialmente para você, vamos entender o que é essa tal vulnerabilidade, como ela funciona, e principalmente como se proteger usando a abordagem mais segura: PDO Prepared Statements (queries preparadas com parâmetros). Sem depender de bibliotecas externas, apenas recursos nativos do PHP. Bora lá?

O que é SQL injection?

SQL injection é um tipo de ataque onde um usuário mal-intencionado "injeta" comandos SQL não previstos dentro de entradas de dados da aplicação (como formulários, parâmetros de URL, etc.), alterando o comando que será enviado ao banco de dados. Em outras palavras, quando a aplicação não trata corretamente os dados fornecidos pelo usuário e simplesmente os concatena na query SQL, um atacante pode inserir trechos de SQL na entrada para modificar a consulta original. O resultado varia de acordo com a criatividade (e malícia) do atacante: ele pode burlar autenticação, obter dados confidenciais e até apagar tabelas inteiras. Tudo isso apenas manipulando a entrada de dados!
Para simplificar, pense em SQL injection como alguém terminando as frases que você começa, mas com más intenções. Por exemplo, você diz ao banco de dados: "Procure pelo usuário cujo nome é X". Se você não delimitar bem o X, um atacante pode fazer a frase ficar assim: "Procure pelo usuário cujo nome é X OU então pegue todos os usuários". Ou pior: "... cujo nome é X; apague todos os usuários". Ou seja, sem proteção, o atacante injeta um complemento malicioso na sua consulta SQL original.

Como funciona um ataque de SQL injection

Vamos entender a lógica por trás do ataque de forma simples. Considere um código PHP ingênuo que verifica login de usuário em um banco de dados MySQL:
// O jeito perigoso (exemplo de código vulnerável) $usuario = $_POST['usuario']; // dado do formulário (não tratado) $senha = $_POST['senha']; // dado do formulário (não tratado) $query = "SELECT * FROM usuarios WHERE usuario = '$usuario' AND senha = '$senha'"; $result = mysqli_query($conexao, $query); // Se retornar algum registro, login é bem-sucedido...
Acima, temos o jeito perigoso de construir uma query: concatenação de strings diretamente com input do usuário. O código pega $usuario e $senha enviados pelo formulário e insere dentro do SQL. Isso parece inofensivo quando o usuário digita algo "normal", como Joao e 123456. A query resultante seria:
SELECT * FROM usuarios WHERE usuario = 'Joao' AND senha = '123456';
Essa consulta busca exatamente o usuário Joao com aquela senha. Até aí, tudo ok.
Mas veja o que acontece se alguém malicioso preenche o campo usuário assim: Joao' OR '1'='1. E deixa o campo senha em branco ou qualquer valor irrelevante. A query montada pelo código vulnerável ficaria:
SELECT * FROM usuarios WHERE usuario = 'Joao' OR '1'='1' AND senha = '';
Repare na armadilha: '1'='1' é sempre verdadeiro! Então a condição usuario = 'Joao' OR '1'='1' sempre passa, independente do usuário. O resultado? O banco retorna todos os usuários (ou pelo menos valida a condição sem verificar senha), permitindo login sem credenciais válidas. Esse é um exemplo clássico de SQL injection para burlar autenticação.
Agora imagine que em vez de apenas conseguir logar, o atacante seja mais agressivo e insira algo como: '; DROP TABLE usuarios; --. Se o código concatenar isso, a consulta pode se transformar em duas: a primeira fechando o SELECT e a segunda deletando toda a tabela de usuários!
A falta de tratamento adequado da entrada permite que comandos inesperados sejam executados no banco.
Resumo da lógica do ataque (clique para expandir):
  1. O desenvolvedor cria uma query SQL juntando strings e dados externos (input do usuário) de forma direta.
  1. O atacante fornece um input cuidadosamente elaborado contendo trechos de SQL especiais (como ' OR '1'='1 ou comandos terminadores ;).
  1. A aplicação vulnerável não sanitiza nem parametriza esse input, inserindo-o cru na consulta.
  1. O SQL resultante passa a ter lógica extra ou comandos maliciosos que não estavam previstos, mas que serão executados pelo banco de dados.
  1. Consequência: acesso indevido, exposição ou destruição de dados – a vulnerabilidade SQL no PHP foi explorada com sucesso.
Percebeu como a lógica por trás do SQL injection é simples? Basicamente, aproveitar a falta de validação/segurança para enviar comandos inesperados ao banco. Mas calma que nem tudo está perdido! Vamos ver agora como se proteger.

Protegendo-se corretamente: Prepared Statements com PDO

Felizmente, proteger aplicações PHP contra SQL injection não é um bicho de sete cabeças. A solução é usar consultas preparadas (Prepared Statements) com PDO (PHP Data Objects) ou, alternativamente, com mysqli (se preferir). A ideia central das prepared statements é nunca misturar dados do usuário diretamente com o comando SQL. Em vez disso, você prepara o comando SQL com placeholders (marcadores para os dados) e depois liga os dados nesses placeholders, mantendo comando e dados separados. Assim, mesmo que o usuário insira ' OR '1'='1 ou qualquer coisa estranha, isso não vai quebrar sua query, pois será tratado apenas como um valor (string literal) e não como parte do código SQL em si.
Vamos reescrever o exemplo anterior usando PDO e prepared statements:
$usuario = $_POST['usuario']; $senha = $_POST['senha']; $pdo = new PDO("mysql:host=localhost;dbname=meu_banco", "meu_usuario", "minha_senha"); // Habilitar o modo de erros do PDO para exceções (boa prática) $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Preparar a query SQL com parâmetros nomeados $stmt = $pdo->prepare("SELECT * FROM usuarios WHERE usuario = :user AND senha = :pass"); // Vincular os valores seguros aos parâmetros (bindValue ou bindParam) $stmt->bindValue(':user', $usuario, PDO::PARAM_STR); $stmt->bindValue(':pass', $senha, PDO::PARAM_STR); // Executar a query com os parâmetros já bindados $stmt->execute(); // Obter resultados (se necessário) $dados = $stmt->fetchAll(PDO::FETCH_ASSOC);
Olha a diferença: na query usamos :user e :pass no lugar onde iriam os dados. Em seguida, usamos $stmt->bindValue() para "ligar" as variáveis $usuario e $senha a esses parâmetros nomeados. Poderíamos usar $stmt->bindParam() também – ambas funções servem para associar valores aos placeholders (a diferença é que bindParam liga a variável por referência, mas para fins práticos aqui o resultado é o mesmo). Também especificamos o tipo dos parâmetros (PDO::PARAM_STR indicando string), o que ajuda o PDO a tratar os dados corretamente. Por fim, chamamos $stmt->execute() sem precisar concatenar nada manualmente.
Outra forma conveniente é usar $stmt->execute() passando um array associativo com os parâmetros, assim: $stmt->execute(['user' => $usuario, 'pass' => $senha]);. Isso elimina a necessidade de chamar bind manualmente para cada parâmetro. Fica a seu critério qual estilo prefere, o importante é parametrizar.
Com essas abordagens, mesmo que $usuario contenha algo como ' OR '1'='1, a consulta será executada de forma segura. O PDO irá enviar ao banco de dados o comando SQL preparado e os valores separadamente. O banco entende que :user e :pass são dados a serem inseridos e não parte do código SQL. Assim, ' OR '1'='1 será tratado literalmente como o texto ' OR '1'='1 (um valor de usuário improvável, que provavelmente não encontra nenhum registro correspondente) em vez de ser interpretado como lógica da consulta. Resultado: o ataque de SQL injection falha – sua aplicação fica protegida.
📽️
Na dúvida se PHP ainda é uma boa aposta em 2025? Dá uma olhada neste bate-papo sobre vale a pena aprender PHP e você vai ver que sim – e parte da razão é exatamente porque a linguagem evoluiu com práticas como essa de segurança embutida.
Video preview

Segurança: um pilar na jornada de um desenvolvedor profissional

Aprender a evitar SQL injection não é apenas aprender "mais uma técnica de programação". É abraçar a segurança como parte integrante do desenvolvimento. Quando a gente começa em PHP, fica empolgado em fazer o código funcionar – imprimir na tela, resolver um desafio de lógica, construir aquele CRUD básico. Isso é ótimo (e se você está nessa fase, aproveite e mergulhe nos fundamentos: desde configurar seu ambiente PHP até praticar com exemplos e pequenos projetos). Mas conforme evoluímos, percebemos que não basta o código funcionar – ele precisa ser confiável, robusto e seguro.
Segurança em aplicações web não é um assunto opcional ou avançado demais: é fundamental. Grandes empresas que usam PHP em seus projetos têm times e processos inteiros dedicados a garantir que falhas como SQL injection não ocorram. Para chegar nesse nível, você como desenvolvedor precisa incorporar esse mindset: toda vez que lidar com input externo (formularios, query params, JSON recebido de API, etc.), pense em como sanitizar ou, melhor, parametrizar essas entradas. Cada consulta SQL deve ser pensada com carinho em termos de segurança. Afinal, nada adianta um site cheio de funcionalidades bacanas se uma vulnerabilidade dessas abre brecha para invasores.
Se hoje você aprendeu a evitar injeção de SQL com prepared statements PDO, parabéns! Você deu um passo importantíssimo rumo a se tornar um desenvolvedor back-end mais sênior e seguro do que a média. Mas não pare por aqui: SQL injection é só uma das questões de segurança no PHP.
Video preview

Próximos passos e uma jornada segura com PHP

Ao chegar até aqui, você já sabe o que é SQL Injection, como identificá-lo em código PHP vulnerável e, principalmente, como se proteger usando PDO e prepared statements.
No processo, também reforçamos a importância de tratar a segurança como parte do dia a dia de programação – tão importante quanto saber estruturar um loop ou uma função. Cada pedaço de conhecimento desses vai aumentando sua confiança para escrever código back-end profissional.
Segurança em PHP não é luxo, é necessidade.
E aí, animado(a) para continuar evoluindo? A jornada não para por aqui. Se você quer solidificar de vez essas e outras habilidades em PHP – desde os fundamentos até construção de projetos completos com as melhores práticas – a Rocketseat tem o caminho pra você. Nossa Formação em PHP foi criada exatamente com esse propósito: ajudar devs a dominarem o back-end com PHP do jeito certo, moderno e seguro, construindo uma base sólida e avançando para tópicos profissionais (bancos de dados, segurança, frameworks como Laravel, tudo isso). Quer dar o próximo passo?
Vamos juntos nessa missão de escrever códigos melhores, mais seguros e conquistar novos patamares na programação. Bons códigos, com segurança, e até a próxima!
Artigos_

Explore conteúdos relacionados

Descubra mais artigos que complementam seu aprendizado e expandem seu conhecimento.