Há alguns dias, me deparei com uma falha de segurança em um site que desenvolvi para uma empresa. Na verdade, não houve qualquer tipo de ataque, mas após ter sido submetido a um conjunto de testes, notei que o site estava vulnerável a uma técnica conhecida como SQL Injection, ou Injeção de SQL. Se você é desenvolvedor web e não conhece este termo, confira o artigo!
Introdução
A maioria dos sites normalmente possuem uma tela de login e senha para acessar um conteúdo ou uma área restrita. No meu caso, é um site que proporciona um sistema on-line para emissão de pedidos, utilizado pelos vendedores da empresa. O processo de login é realizado de modo tradicional, validando os dados do usuário em uma consulta SQL da seguinte forma:
O usuário entra com o login e senha em campos do tipo “text” e “password”, respectivamente:
1 2 |
<input type="text" name="login" id="login" maxlength="20" > <input type="password" name="senha" id="senha" maxlength="10"> |
Ao pressionar ENTER ou clicar no botão de login, o site leva os dados digitados para uma página PHP:
1 2 |
$login = $_POST['login']; $senha = $_POST['senha']; |
Essa página, por sua vez, verifica se o login e senha existem no banco de dados, através de uma SQL:
1 |
Select * from Usuarios where Login = '$login' and Senha = '$senha' |
Se os dados estiverem corretos, a página principal da área restrita é exibida.
No entanto, apesar de funcional, o código acima não está preparado para evitar uma tentativa de SQL Injection!
Definição
O SQL Injection consiste em anexar uma condição SQL dentro do código-fonte. Aqui, poderÃamos literalmente utilizar a palavra “injetar”. Por meio dessa técnica, é possÃvel adicionar uma condição OR na instrução SQL para que, mesmo que o usuário e senha estejam inválidos, o Select retorne um resultado verdadeiro. Em outras palavras, é uma forma de alterar a instrução SQL original para burlar a validação de segurança de um site. Este assunto é bastante discutido na comunidade de desenvolvimento web em virtude da sua importância e do risco relacionado à invasão de sites.
Mas o que isso tem a ver com o código PHP?
Eis que chegamos no objetivo do artigo. Na página de login, o que acontece se digitarmos a condição SQL abaixo nos campos de login e senha?
1 |
' or 1 = 1 |
Exato! A instrução Select retornará verdadeiro! Ao invés dos dados de login e senha, haverá uma instrução SQL para “enganar” o banco de dados. Traduzindo em código, observe o resultado da instrução SQL que será executada no código PHP:
1 |
Select * from Usuarios where Login = '' or 1 = 1 and Senha = '' or 1 = 1 |
Note que há duas condições OR que verificam se 1 é igual a 1.
E qual a finalidade dessa condição?
Simples. Vamos analisar a SQL em partes:
1 |
Login = '' or 1 = 1 |
Nessa condição, o Select irá retornar verdadeiro caso o login seja em branco (vazio), ou caso o número 1 seja igual a 1. Porém, como a segunda condição SEMPRE será verdadeira, a primeira condição é invalidada. O mesmo acontece para a senha:
1 |
Senha = '' or 1 = 1 |
Observe que, mesmo que o login e senha estejam incorretos, as condições OR sempre serão atendidas. Na prática, a instrução SQL se comportará dessa forma:
1 |
Select * from Usuarios where 1 = 1 and 1 = 1 |
Logo, ao executar essa SQL, a consulta retornará um estado verdadeiro e o usuário terá acesso à área restrita. Assustador, não?
Solução
Graças aos recursos do PHP, há uma função chamada addslashes que adiciona barras invertidas antes de qualquer caractere de escape, como as aspas simples. Se utilizarmos essa função no nosso código, o SQL Injection perderá o efeito! Portanto, vamos modificar o código que recebe os dados do login e utilizar o addslashes:
1 2 |
$login = addslashes($_POST['login']); $senha = addslashes($_POST['senha']); |
Como impacto, a instrução SQL também será afetada e o retorno será falso:
1 |
Select * from Usuarios where Login = '' or 1 = 1 and Senha = '' or 1 = 1 |
O addslashes é uma das alternativas para impedir o SQL Injection. Além dessa opção, você também pode, por exemplo, substituir as aspas simples por uma string vazia, o que também irá invalidar o SQL Injection. Porém, atente-se que neste caso, os usuários que tiverem aspas simples no login ou na senha terão problemas ao acessar a página. Outra alternativa é tratar essa ameaça no próprio código-fonte, criando uma função que verifique possÃveis cláusulas SQL nos dados recebidos.
Independente da solução utilizada, é muito importante realizar vários testes para conferir se a brecha se segurança realmente foi corrigida.
Antes de finalizar, gostaria de fazer um agradecimento especial ao meu irmão, Danilo Celestino, pela orientação nessa questão do SQL Injection e pela ajuda para solucioná-lo, o que me motivou a elaborar este artigo!
Agradeço pela visita, leitores! Abraço!
Para quem não conhece ainda, o php criou uma função do mysql para chamada “mysqli”, utilizando essa função , não é mais preciso ficar tratando de forma POG para não ocorrer um SQL Injection, só estou exemplificando do mysql, para Sql Server, existe uma Extensão do php “sqlsrv” que tem o mesmo objetivo do “mysqli”, evitar SQL Injection. Maioria usadas em POO, pois são de mais facil integração e implementação.
Fala, Enzo! Obrigado pelo comentário e pela contribuição com o seu conhecimento. Já ouvi falar dessas funções especiais para evitar o SQL Injection, embora eu ainda não tenha colocado elas em prática. Abraço!
Opa o Danilo celestino conheço rsrs, e parabéns pelo artigo, apesar de simples quem usa programação na unha ainda deixa passar estes requisitos básicos de segurança.
Parabéns pelo artigo.
Obrigado por deixar o comentário, Wanderson! Realmente, a segurança é um requisito importante para o desenvolvimento web e deve ser devidamente considerada pelos programadores.
Parabéns amigo, excelente blog. Já repassei a alguns amigos que estão estudando programação para poder entrar no mercado de trabalho.
Está sendo de imensa ajuda suas explicações.
Tenha uma excelente semana!
Olá, Allan!
Muito obrigado pelo comentário! Fico feliz em saber que o blog está ajudando vocês!
Um grande abraço!