segunda-feira, 30 de abril de 2012

Segurança contra SQL Injection


Ao desenvolver um sistema web, é quase certo que serão construídas queries SQL (a menos que use um framework que faça isso por você}. Tipicamente, o que ocorre é:
1. Você cria um formulário que permite ao usuário submeter alguns dados;
2. Seu script recupera os dados informados pelo usuário, e faz uma atribuição da mesma (global) à uma variável local, como no exemplo abaixo:
$nome = $_POST['nome'];
3. Agora o seu script usa o dado digitado pelo usuário, como parâmetro da query SQL.
$sql = "SELECT * FROM usuarios WHERE nome = '{$nome}'";
4. O seu script submete a consulta ao servidor de banco de dados.
5. O servidor de BD retorna o resultado da consulta, que por sua vez é exibida ao usuário final.

Este é um processo muito comum, e o que geralmente muda são os parâmetros e a consulta - no entanto o processo é geralmente o mesmo (ou muito similar).

O problema está no momento em que a variável informada pelo usuário for intencionalmente forjada, como a seguir:

admin' or 1=1

o resultado da consulta é muito diferente do que normalmente se espera. De fato, agora são duas instruções em vez de uma: O que pode ocorrer neste caso, é que a consulta executada traga todos os registros da tabela usuarios. Dependendo da forma como estes dados são apresentados, é muito possível que o atacante possa obter acesso a dados além do que lhe seria permitido. O mesmo processo pode ser usado para burlar mecanismos de autenticação, obtendo a credencial do administrador por exemplo, sem ter a senha.
Veja um exemplo de uma forma de ataque, que tenta injetar

admin'; GRANT ALL ON *.* TO 'BadGuy@%' IDENTIFIED BY 'gotcha

Embora a lista possa não recuperar nenhum registro, a consulta poderá criar uma autorização de acesso para o atacante. Este problema foi alvo de discussões especialmente no banco de dados MySQL - usado na grande maioria dos sites. Mas, o mesmo pode ocorrer com PostrgreSQL, SQLServer e até Oracle.

Formas de prevenir o ataque de SQL Injection

1. Delimite bem as consultas, colocando as variáveis do tipo string entre aspas;
2. Verifique os tipos de dados enviados pelo usuário (string, numérico, data, etc), usando as funções intval(), gettype(), is_int(), etc.
3. Valide o tamanho das variáveis entradas pelo usuário
4. Verifique a presença de caracteres suspeitos no conteúdo enviado pelo usuário (como ponto-e-vírgula, aspas simples e duplas, por exemplo).
5. Processe as variáveis, usando funções como addslashes(), por exemplo - isso quando aspas são admitidas, mas não podem ser "confundidas" pelo servidor de banco de dados.
6. Crie uma camada de abstração para melhorar a segurança (uma classe intermediária pode ajudar a esconder detalhes da estrutura do banco de dados, por exemplo).
7. Use os métodos de preparação das consultas, disponíveis nas funções de acesso ao banco de dados: No exemplo anterior:
$sql = "SELECT * FROM usuarios WHERE nome = ? ";
$stmt = mysqli_prepare( $connect,$sql);
if ( $stmt ) {
mysqli_stmt_bind_param( $stmt, "s", $usuario );
}

8. Teste sua aplicação contra um ataque.

Nenhum comentário: