Impedir a injeção SQL no PHP (e outras vulnerabilidades)

Se você tem sido em torno de desenvolvimento web por um tempo, você quase certamente, ouvido o termo “injeção de SQL” e alguns terríveis histórias sobre ele.

o PHP, como muitas outras linguagens, não está imune a esse tipo de ameaça, o que pode ser muito perigoso. Mas, felizmente, proteger seus sites da injeção de SQL e outras ameaças semelhantes é algo que você pode dar passos tangíveis.

neste post, você aprenderá o que é injeção SQL, quais são as consequências de um ataque bem-sucedido, como um invasor pode aproveitar o código PHP vulnerável, o que você pode fazer para evitá-lo e quais ferramentas você pode usar para detectar as partes do seu código que podem estar sujeitas a esse tipo de ameaça. Além disso, você aprenderá sobre algumas outras vulnerabilidades comuns das quais deve estar ciente para criar aplicativos mais seguros.

como funciona uma injeção SQL?

a primeira coisa que você precisa saber para proteger seu código da injeção SQL é entender como ele pode ser explorado por um invasor.

a ideia por trás do exploit é bastante simples: um invasor executa código SQL malicioso em seu banco de dados por meio de seu aplicativo.

como alguém poderia conseguir isso? Abusando das entradas do seu aplicativo.

vejamos um exemplo simples. Suponha que você tenha um aplicativo mostrando uma lista de nomes de usuário, onde cada usuário tem um link para seus detalhes como este:

e então, no arquivo user_details.php, você tem isso:

<?php $sql = "SELECT * FROM user WHERE id = ".$_GET;

se o usuário agir conforme o esperado e clicar no nome de algum usuário, o URL da solicitação ficará assim: http://domain.com/user_details.php?id=8.

então, o que há de errado com isso?

o valor de $sql será a string "SELECT * FROM user WHERE id = 8" e a consulta será executada muito bem.

o problema está na parte em itálico desta frase: Se o usuário agir como esperado e clicar no nome de algum usuário, o URL da solicitação ficará assim:

http://domain.com/user_details.php?id=8.

e se o Usuário não agir como esperado?

e se alguém fabricasse um URL enviando algo diferente de um número como o ID?

URLs fabricados

se o URL fosse algo como http://domain.com/user_details.php?id=Peanuts, a string SQL resultante seria: "SELECT * FROM user WHERE id = Peanuts".

neste caso, o problema seria para o usuário. A consulta simplesmente falharia e, na pior das hipóteses, o usuário veria algum tipo de mensagem estranha.

mas e se o URL fosse mais parecido com este:

http://domain.com/user_details.php?id=1+OR+1%3B? (Este é o resultado de urlencode('1 OR 1;'), a propósito.)

neste caso, o SQL resultante seria "SELECT * FROM user WHERE id = 1 OR 1;". Isso significa que o resultado da consulta seria cada usuário no banco de dados em vez de apenas o usuário cujo ID é igual a 1. Ai.

mas isso ainda não é muito ruim, certo?

agora vamos tentar um exemplo mais extremo.

vamos supor que o URL era algo como

http://domain.com/user_details.php?id=1%27%3B+DROP+TABLE+users%3B--+, que se traduz em id=1; DROP TABLE users;--, produzindo efetivamente este comando SQL:

"SELECT * FROM users WHERE id = 1; DROP TABLE users;-- ". E se você executar esta consulta … Big ouch.

caso você não esteja 100% familiarizado com a sintaxe SQL, DROP TABLE excluiria completamente a tabela. Seria como se nunca existisse.

claro, um backup recente ajudaria a reduzir o dano, mas ainda assim. Isto é mau.

se você quiser saber mais sobre esse tipo de ataque, há um ótimo post aqui.

mas acho que isso é uma má notícia. Vamos passar para um lugar mais alegre: como você pode evitar esses tipos de injeções de SQL em seus aplicativos PHP.

como evitar injeções de SQL em PHP

como você viu, o problema é quando um invasor tem a capacidade de executar código SQL sem que você esteja ciente disso.

esta situação se apresenta como consequência de ter consultas criadas em tempo real por concatenação de string e incluindo dados coletados de entradas externas (geralmente entrada do usuário, mas também outras fontes externas, como arquivos, respostas a chamadas de API e assim por diante).

portanto, para evitar essa vulnerabilidade em seu código, você precisa corrigir as fontes de possíveis problemas.

Validar as entradas

Uma maneira simples para corrigir a vulnerabilidade no exemplo acima, seria verificar se o parâmetro ID é o que se espera dele (i.e., um inteiro positivo):

outra maneira de fazer esse tipo de validação é aproveitar os filtros internos do PHP:

<?php
$id = filter_input( INPUT_GET, 'id', FILTER_VALIDATE_INT);

portanto, ao validar suas entradas, você evita que invasores executem código malicioso junto com o seu.

não há uma maneira fácil de impedi-los de tentar, mas contanto que suas tentativas não sejam bem-sucedidas, você está pronto para ir.

Use instruções preparadas

outra maneira de proteger seu código contra injeções SQL é usando instruções preparadas. Instruções preparadas são comandos SQL pré-compilados.

eles podem ser usados com uma biblioteca de acesso a banco de dados específica (como mysqli) ou com a biblioteca PDO mais genérica.

vamos dar uma olhada em um exemplo usando mysqli:

se você tentar este código (substituindo as credenciais do banco de dados, é claro), você verá que, se acessado por meio de um URL legal como http://domain.com/user_details.php?id=1, você obterá o mesmo resultado que qualquer um dos URLs maliciosos acima (que são as informações sobre o usuário associado ao ID 1 e nada mais).

se você preferir usar o PDO, o código ficará mais parecido com este:

não há muita diferença, certo?

em resumo, as instruções preparadas oferecem uma ótima maneira de evitar injeções de SQL. Além disso, eles geralmente têm um desempenho melhor do que o SQL on-the-fly.

agora que você conhece a ferramenta, você só precisa usá-la.

como detectar código PHP vulnerável a injeções de SQL

uma vez que a vulnerabilidade é encontrada, corrigi-la não é realmente complicado. Mas como você encontra esses tipos de vulnerabilidades em seu código em primeiro lugar?

bem, se você tiver sorte e sua base de código for pequena o suficiente, uma simples revisão do Código fará.

mas se o seu aplicativo for de tamanho médio a grande, você precisará de ajuda extra.

uma ferramenta muito popular (e de código aberto) que você pode usar para esse fim é o sqlmap, um script Python simples que tentará atacar qualquer URL que você alimentar e relatar suas descobertas.

aqui está uma saída de amostra de uma corrida contra um site baseado em PHP meu:

outra ferramenta de código aberto que você pode usar é o Arachni, uma ferramenta mais sofisticada que inclui uma GUI da web e foi escrita em Ruby.

aqui está uma saída de amostra de executá-lo através da CLI:

além da revisão e teste de código, você deve considerar o uso de uma solução RASP, como o Sqreen, para monitorar seus ambientes de produção para interromper a execução de quaisquer exploits que passem pelos estágios de revisão e teste.

quais outras vulnerabilidades você deve estar ciente?

injeções de código

SQL injection é apenas um membro de uma família maior de vulnerabilidades: injeção de código.

a ideia por trás de diferentes técnicas de injeção de código é sempre a mesma. É tudo sobre enganar um aplicativo inocente para executar código malicioso.

isso pode ser feito de várias maneiras.

em PHP, existem funções projetadas para simplesmente emitir comandos diretamente no sistema operacional, como a função exec.

Aqui está um exemplo simples de como isso pode ser explorado:

<?php
exec( 'cp uploads/'.$_GET.' /secure/');

Se alguém tiver pedido http://domain.com/exec.php?file=a.txt%3B+rm+-Rf+%2A+%23, o resultado seria a execução deste comando:

cp uploads/a.txt; rm -Rf * # /secure/

E isso pode acabar com todo o disco no servidor—não validado de entrada ataca novamente!

Cross-site scripting

outra vulnerabilidade muito comum é cross-site scripting (XSS). Nesse caso, a vítima é o usuário que visita seu site.

XSS é quando um invasor injeta código JavaScript malicioso dentro de um formulário HTML regular, que mais tarde será renderizado pelo navegador de outro usuário.

Olhar para este script PHP:

<?php
echo "<html><p>Hello {$_GET}!</p></html>";

Claramente, a intenção aqui é criar uma página simples que cumprimenta o usuário.

mas quando os bandidos chegam a ele, o que os impede de solicitar

http://domain.com/greet.php?name=Mauro%3Cscript+language%3D%22javascript%22%3Ealert%28%22You+are+hacked%21%22%29%3B%3C%2Fscript%3E? Este pedido irá produzir o seguinte HTML como saída:

<html><p>Hello Mauro<script language="javascript">alert("You are hacked!");</script></p></html>

Este é, claramente, uma ingênua explorar—afinal, qual é o mal em mostrar a alguém um simples “Você está cortado!”mensagem? Mas as coisas podem ficar muito sérias com um pouco de imaginação. Lembre-se de que cada solicitação feita a um servidor inclui o cookie de sessão, portanto, essa vulnerabilidade pode ser a culpada por trás das sessões de seus usuários serem sequestradas.

então, novamente, enquanto os problemas parecem feios, as correções são realmente simples: é tudo sobre validação e sanitização.

no caso do XSS, uma chamada simples para htmlentities antes de produzir a saída real Serve.

corrigir vs. detectar vulnerabilidades de segurança

como você viu neste post, corrigir vulnerabilidades de segurança—injeções SQL e outros tipos—não é o principal desafio. Percebendo que seu código é vulnerável e onde pode ser explorado, é. Você tem algumas opções aqui:

  • muito forte disciplina de codificação
  • Teste seus aplicativos completamente por questões de segurança
  • Alavancagem de monitoramento e ferramentas de proteção para prevenir exploits e receba alertas em tempo hábil

Qual for a sua escolha é até você. O importante é prestar atenção a esses problemas e agir com base nesse conhecimento. Fica em segurança.

Leave a Reply

O seu endereço de email não será publicado.