-1

Anti SQL Injection

criado por Herbert Araujo em 18/12/2003 10:23am
O SQL-Injection é um ataque que visa enviar comandos nocivos à base de dados através dos inputs dos formulários ou através da query string (URL).

Caso o seu site não esteja "vacinado" contra o SQL-Inject um ataque bem sucedido pode, entre outras coisas, dropar (apagar) uma tabela do banco; deletar todos os dados da tabela; conseguir senhas caso existam na tabela; inserir caracteres inválidos no banco; etc.

Regra: Valide sempre todo e qualquer tipo de dado que as páginas do site estiverem esperando, seja via formulário (post) ou via query string (get).

Bom, vamos à função:

Primeiro declaro a função, note que recebe um parâmetro. Esse parâmetro é o texto que vou validar. Como disse esse texto pode vir de um formulário ou passado através da URL.

<?php
function validatxt($txt) {
    $inject=0;
Aqui declaro um array com as palavras que são consideradas nocivas para o banco, ou seja, comandos SQL. Todas as palavras do array serão recusadas. Você pode acrescentar outras palavras, por exemplo, se o seu formulário for de cadastro, você pode usar a função para barrar algumas palavras que não queira cadastrar no banco como palavras reservadas ou palavrões.

Note que existe um espaço antes e depois de cada comando do sql. Isso é proposital já que a palavra sozinha, por exemplo "update" não causaria nenhum mal ao banco, se o comando fosse usado teria que ser feito da seguinte maneira: "update <tabela> set...", teria um espaço entre o comando e o nome da talela.

$badword = array(" select","select "," insert"," update","update "," delete","delete "," drop","drop "," destroy","destroy ");

Agora eu faço um loop para percorrer todo o meu array de palavras. Eu controlo o loop com a função sizeof(), que mostra quantos índices (palavras) tem meu array. A função substr_count() verifica se a minha palavra atual $badword[$i] está contida no texto que foi passado para a validação. Se a função retornar true então existe uma palavra do meu array dentro do texto passado. Nesse caso eu altero o valor da variável $inject para 1.

    for ($i=0;$i<sizeof($badword);$i++){
        if (substr_count($txt,$badword[$i])!=0){
            $inject=1;
        }
    }

Aspas, apóstrofe, sinal de menor ou de maior ou outros caracteres também devem ser validados pois são caracteres que não devem ser levados ao banco. Ao contrário do meu array de palavras inválidas eu declaro um array de caracteres válidos. Todo caractere que não faz parte do meu array será recusado pela função. Aqui você também pode acrescentar ou retirar caracteres conforme a sua necessidade. Note que o último caractere é um espaço em branco, isso é importante para que a função aceite palavras compostas (guarda chuva é uma palavra composta). Se quiser recusar esse tipo de palavra retire o espaço do final do array.

$charvalidos = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ÁÀÃÂÇÉÈÊÍÌÓÒÔÕÚÙÜÑáàãâçéèêíìóòôõúùüñ!?@#$%&(){}[]:;,.- ";

A função strlen() controla o meu loop, ela retorna o número de caracteres que a palavra que estou validando tem. A substr() retorna uma parte da palavra, nesse caso um caractere, é por aqui que eu percorro letra por letra da palavra a ser validada. Com a substr_count() eu verifico se o caractere é aceito ou não, se não for eu altero valor da variável $inject para 1.

    for ($i=0;$i<strlen($txt);$i++){
        $char = substr($txt,$i,1);
        if (substr_count($charvalidos,$char)==0) {
            $inject=1;
        }
    }

Retorno a variável $inject para informar à minha página se a palavra é inválida ou tem algum caractere inválido.

    return($inject);

// Fim da função
}
?>

Ficou um pouco extenso mas foi necessário. Se ficou alguma dúvida me mande um e-mail. Espero que a função venha a ser útil para alguém.

Nesse link coloquei um exemplo funcional:
http://www.php.locaweb.com.br/araujo/inject.php

Aqui você faz o download do script pronto:
http://www.phpbrasil.com/scripts/script.php/id/1359

Comentários:

Mostrando 1 - 10 de 15 comentários
Herbert...axu q se vc fizer +ou- assim dá:

<?

session_start();

$nome_grupo = $_POST['nome_grupo'];

If(!$nome_grupo) //mostra formulario para inclusao dos dados do grupo a ser cadastrado
{

// inicializa a ficha sincronizadora com um valor aleatorio
$_SESSION["synctoken"]= uniqid(time( ));

echo "CADASTRAR GRUPO<br><br>";

echo "<form method=post action=".$_SERVER['PHP_SELF'].">";

echo "Nome: <input type=text name=nome_grupo></input><br>";
echo "<input type=hidden name=token value=".$_SESSION["synctoken"].">";

echo "<input type=reset value='Limpar'></input><input type=submit value='Enviar'><br>";

echo "</form>";

}
Else //cadastra grupo
{

If($_POST['token'] != $_SESSION['synctoken'])
{
echo "Operacao Cancelada! Tentativa de invasao ou duplicacao";
exit;
}
//troca valor da ficha de sincronizadora
$_SESSION["synctoken"]= uniqid(time( ));

$sql = "INSERT INTO grupo (nome_grupo) VALUES ('".$nome_grupo."')" ;
$resultado = pg_exec($sql);

}


?>
29/09/2005 1:28pm (~18 anos atrás)

Lauro, perfeito essa sua técnica, mas ela só funciona se você tiver dois arquivos separados, ou seja, um arquivo para o fomulário e outro para a parte dinâmica (que receberá os dados). Se você usar apenas um arquivo que chama ele mesmo quando o usuário submeter os dados, esse esquema falha. Como você faria para resolver isso?
28/12/2004 10:26am (~19 anos atrás)

Como o gerador de ID é randômico a chance dos dois IDS se coincidirem é praticamente nula. Feito isso, é colocar o javascript pra funcionar e não permitir entrada de caracteres inválidos em nenhum campo. Como o invasor geralmente mata todo o codigo java do form e salva a pagina em seu micro, para então colocar os codigos nocivos, o sistema vai recusar o id do form e sua página vai estar protegida.
26/02/2004 10:35am (~20 anos atrás)

Uma técnica que ajuda tb é criar id's para os forms. tipo:

$formID= (int)mt_rand(1234567890,9876543210);
$_SESSION['idForm']=$formID;

// gera um numero randomico, salva em session e identifica o form com esse id
<input type="hidden" name="idForm" value="<?= $formID ?>">

//no retorno do form

if ($_POST['submitcad']) {
// recupera o id do form
$formID=(int)$_POST['idForm'];
// verifica se coincide com o valor da session
if (!$_SESSION['idForm'] || $formID != $_SESSION['idForm']) {
// se nao coincide, salva numa tabela os dados do usuario como ip, data, de onde veio, etc...
// savelog grava um log na tabela com todos os dados do usuario.
savelog($conn);
mysql_close($conn);
die("Seu IP foi capturado para averiguação por tentativa de ataque via SQL Injection a este site.<br><br>Acesso não autorizado!");
}

Abraços
26/02/2004 10:31am (~20 anos atrás)

Paulo disse:
Acho que seria mais conveniente dar uma olhada nas funções que já estão implantadas no PHP. Por exemplo, para mysql pode-se usar a função:

mysql_escape_string
(PHP 4 >= 4.0.3)

mysql_escape_string -- Escapa uma string para uso com o mysql_query.



Para PostGreSQL:


pg_escape_string
(PHP 4 >= 4.2.0)

pg_escape_string -- Gera string para o tipo text/char
20/02/2004 7:39am (~20 anos atrás)

Baseando na versao do Herbert, criei a minha versao...ficou assim:

<?php

function CheckInput(&$txt) {
$inj=0;
$badword = array(" select","select "," insert"," update","update "," delete","delete "," drop","drop "," destroy","destroy ");

for ($i=0;$i<count($badword);$i++){
if (substr_count($txt,$badword[$i])!=0){
$inj=1;
}
}
$txt = mysql_escape_string($txt);
return($inj);
}
// Exemplo
$str = $_GET['str'];
if (!CheckInput($str)) {
echo $str;
}

?>

Nessa versao, ele já faz o escape e a checagem dos comandos SQL.
26/01/2004 6:16am (~20 anos atrás)

A validação deve ser feita sempre em todos os campos!!!
gosto de validar da seguinda forma
$campo = addslashes(trim($campo));
simples e show de bola
09/01/2004 9:11pm (~20 anos atrás)

você pode validar os dados tb utilizando
stripslashes(trim($variável));

Assim além de remover barras de escape remove o espaçamento da variável que torna as palavras perigosas resolvendo essa parte de badwords
09/01/2004 12:12pm (~20 anos atrás)

Pessoal, então pelo que eu entendi disso tudo quando eu enviar um formulário tenho que fazer essa validação em todos os campos de texto do formulário antes de ser armazenado no banco, certo?
06/01/2004 7:13am (~20 anos atrás)

isso serveria para usuarios de phpnuke? como?
04/01/2004 2:07am (~20 anos atrás)

Novo Comentário:

(Você pode usar tags como <b>, <i> ou <code>. URLs serão convertidas para links automaticamente.)