Proteção contra o SQL Injection

Olá a todos, desculpem pela ausência, por algum motivo (algo relacionado com o Speedy) eu não estava conseguindo acessar o meu blog o que me impossibilitava de escrever, i’m sorry.

Hoje vou falar um pouco sobre como funciona o SQL Injection e como podemos proceder para que nós não sejamos pegos por este tipo de vulnerabilidade.

O que é SQL Injection?

SQL Injection é uma técnica de injeção de código que explora uma vulnerabilidade de segurança localizada na camada de banco de dados (database layer) do servidor. Na prática é utilizada por hackers (ou não) para ter acesso ao banco de dados do servidor da vítima sem que ele precise de um login ou uma senha efetivamente para fazê-lo.

Como funciona o SQL Injection?

Para entendermos melhor como funciona os ataques via injeção de códigos SQL (SQL Injection), vou utilizar um caso bem comum entre os programadores para que vocês se familharizem com mais facilidade.

Pense em um formulário para login no sistema e uma consulta na base de dados para verificar se determinado usuário e senha existe em na base de dados.
Exemplo
SELECT usuario,senha FROM usuarios WHERE usuario = 'igorescobar' AND senha='123456'

O ataque acontece justamente no ponto, aonde o usuário preenche o usuário e a senha no formulário de login do site. Se você não filtra todos os dados que vem de FORA para DENTRO da sua aplicação, você corre o risco de ser atacado.

Na prática o código fica algo parecido com isso:
SELECT usuario,senha FROM usuarios WHERE usuario = '$usuario' AND senha='$senha';

Estas variáveis ($usuario e $senha) estão vindo do $_POST do seu formulário e o servidor iria interpretar assim:
SELECT usuario,senha FROM usuarios WHERE usuario = 'igorescobar' AND senha='123456';

Agora imagine que eu sou um usuário mal intencionado e desejo logar no servidor de vocês sem ser autorizado.

No campo senha vou inserir o seguinte conteúdo:
123456' OR 'a'='a

Vamos ver como fica?
SELECT usuario,senha FROM usuarios WHERE usuario = 'igorescobar' AND senha='123456' OR 'a'='a';

Hun… como diria eu mesmo (há!): F-U-D-E-U!

Quer outro exemplo? … um mais bacana?

Agora no campo senha eu vou inserir o seguinte conteúdo:
123456'; DROP TABLE usuarios; --

Vamos ver como fica?
SELECT usuario,senha FROM usuarios WHERE usuario = 'igorescobar' AND senha='123456'; DROP TABLE usuarios; --';

Ok, agora você já esta pronto para passar no RH :)

Oh my god :O como eu me protejo dos ataques SQL Injection ?

Primeiro: Separe sempre a exibição de erros em 2 ambientes, o ambiente de desenvolvimento e o ambiente de produção. O ambiênte de desenvolvimento pode ter qualquer tipode erro emitido na tela, afinal, você precisa ver os erros para tratá-los, no ambiênte de produção omita qualquer tipo de erro, qualquer erro pode ser uma pista para o hacker descobrir detalhes sobre o seu ambiênte.

Ambiente de Desenvolvimento
[php]<?php error_reporting(E_ALL ^ E_NOTICE); ?>[/php]

Ambiente de Produção
[php]<?php error_reporting(0); ?>[/php]

Segundo: Filtre todo o tipo de variável dados que veem de urls ou inputs de formulário $_GET ou $_POST para que nenhum dos dados inputados pelo usuário possa ser interpretado como parte da instrução SQL.

Vale lembrar que o certo seria utilizarmos PDO que já tem uma proteção definitiva contra isso, pois ele tem o acesso ao modelo do seu banco de dados e pode fazer muito melhor do que uma simples filtrarem generica nos campos, ele pode filtrar cada campo dependendo do tipo de cada campo o que é muito melhor.

Caso você não use PDO, você pode utilizar também uma função chamada mysql_real_escape_string que também cumpre o que promete.

Solução definitiva para o SQL Injection
Está solução é válida apenas para as pessoas que não utiliza nenhuma das 2 (duas) soluções citadas acima.

[php]
<?php

/**
* Protege o banco de dados contra ataques de SQL Injection
*
* Remove palavras que podem ser ofensivas à integridade do banco
* Adiciona barras invertidas a uma string
*
* @uses $_REQUEST= _antiSqlInjection($_REQUEST);
* @uses $_POST = _antiSqlInjection($_POST);
* @uses $_GET = _antiSqlInjection($_GET);
*
* @author Igor Escobar
* @email blog [at] igorescobar [dot] com
*
*/

function _antiSqlInjection($Target){
$sanitizeRules = array(‘OR’,’FROM’,’SELECT’,’INSERT’,’DELETE’,’WHERE’,’DROP TABLE’,’SHOW TABLES’,’*’,’–‘,’=’);
foreach($Target as $key => $value):
if(is_array($value)): $arraSanitized[$key] = _antiSqlInjection($value);
else:
$arraSanitized[$key] = (!get_magic_quotes_gpc()) ? addslashes(str_ireplace($sanitizeRules,"",$value)) : str_ireplace($sanitizeRules,"",$value);
endif;
endforeach;
return $arraSanitized;
}

?>
[/php]

Terceiro: Não tem, só estes 2 passos está bom ;)

ps: Desculpem as piadas, estou de bom humor hoje :)

UPDATE(1): Acrescentei algumas palavras na blacklist da função
UPDATE(2): Troquei o str_replace para o str_ireplace e acrescentei a verificação de magic_quotes.
UPDATE(3): Retirei a função trim por não fazer diferença alguma rs.

[]’s
Igor.

//

14 thoughts on “Proteção contra o SQL Injection

  1. Remover palavras não é um bom uso.
    Por exemplo, no caso do meu site ser um blog sobre desenvolvimento eu não poderia escrever sobre mysql, pq sempre que eu escrever uma query essa função iria desmontar ela …
    Ou um fórum de desenvolvimento, um post sobre sql seria inútil pq a função não permitiria que uma querya fosse explicada..

    Like

  2. Você poderia substituir as string ‘ “/ para escapar as mesmas e não teria a necessidade de remover palavras chaves da query. Sem duvidas que o PDO para trabalhar com o banco de dados é a melhor opção mas em muitos sistemas antigos não tem como trocar tudo.
    O seu blog está de parabéns gostei muito dele, não conhecia até então.

    😉

    Like

  3. Muito bom o seu artigo, Igor. Eu escrevi uma função recursiva muito parecida com a sua para tratamento de SQL Injection, chega a ser espantoso de tão parecida. Uma das diferenças da minha função é que o tratamento de palavras pode ser desativado e, em casos onde o desenvolvedor queira permitir o envio de somente algumas palavras relacionadas à SQL, pode-se também enviar como parâmetro um array contendo somente o conteúdo que deve ser tratado.

    Like

  4. Igor, parabéns pelo seu script! me protegeu vários sites aqui de clientes 🙂

    Mas tenho um problema em alguns portaizinhos meus, onde faço uma contagem de usuários online, e pico de usuários, e através das strings utilizando $_SERVER a segurança pode ser quebrada…. Por exemplo:

    $ip = $_SERVER[“REMOTE_ADDR”]; ou
    $ip = getenv(“REMOTE_ADDR”);
    e também
    list($utime, $time) = explode(” “, microtime());

    $query = (“SELECT * FROM table WHERE ip = ‘$ip’ “);

    Alguma dica de como aprimorar o script para barrar o SQL injection aqui também? Já tentei remover aspas simples nestas variáveis, mas nada…

    Abraços!

    Like

  5. Olá, gostei do post. Mas, posso perguntar onde vou colocar este include? Já criei este arquivo como antinjection.php. Agora, seria incluir o ‘inlude’ desta página em todas as páginas do site? ou no meu caso, no header.php por exemplo, já seria suficiente, pois este é chamado em todas as páginas.

    Favor me esclarecer.

    Like

  6. Ola Igor, obrigado pela solução mais digamos que eu não entendo nada de php então não so colar a sua “function”o que tenho que alterar nela pra funcionar no meu arquivo de login que funciona assim
    $resultado = mysqli_query($con, “SELECT * FROM `users` WHERE `login`= ‘$login’ AND `senha`= ‘$senha'”);

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: