+1

Evitando SQL Injection com Type Cast no PHP

criado por Ibrahim S. M. Brumate em 19/01/2011 4:52pm
Ola pessoal do PHP Brasil. Este é meu primeiro artigo e vai tratar de um problema muito grave que são as SQL Injection. Todos nós já observamos uma série de tentativas de evitar o problema. Porém nem sempre todas são conclusivas.

A quem diga que só a função addslashes do PHP resolva. A quem diga que só mysql_real_escape_string resolve o problema ou habilitando o magic_quotes. Porém estão errados.

Como já foi dito em outros artigos e fóruns por ai a fora, em uma query
a tipagem tem que estar correta. String com aspas e valores numéricos SEM ASPAS.

"Mas não funciona com aspas?" Sim funciona mas sabemos que resolver o problema com uma gambiarra destas é terrível. Não se grava string em campo com número inteiro. O MySQL vai lhe retornar erro caso queira gravar a string "Eu faço ca...da" em um campo INT. Logo (e não só por este motivo) fazer

SELECT * FROM tabela WHERE id = '1'

é uma perjuria contra as boas praticas de programação.

Mas nem tudo neste sentido está acabado. O fato do campo INT não aceitar strings pode ser uma arma ao nosso favor para evitar injections (mas não atribuindo o valor com aspas sempre pois é errado!).

magic_quotes está obsoleto e será removido do PHP 6.
mysql_real_escape_string só serve para MySQL. Se for um Banco Postgre já não resolve.
addslashes sozinho não evita injection onde temos valores numéricos.

Vejam um exemplo de injection que poderia dar um break no banco de dados por 10 segundos atrasando uma query que pode travar um site com um grande volume de acessos:

<?php 
mysql_connect("localhost","root",""); 
$max = addslashes($_GET['max']);

$s = "SELECT * FROM mysql.user WHERE max_connections = {$max}";

echo $s;

$q = mysql_query($s); 

$a = mysql_fetch_array($q); 

print_r($a); 
?>

Ao fazer isto temos a seguinte situação:

localhost/test.php?max=0+AND+SLEEP(10)

Este tipo de injection vai fazer com que sua query fique desta maneira:

SELECT * FROM mysql.user WHERE max_connections = 0 AND SLEEP(10)"

Como podem ver o número inteiro deixa uma brecha para que o injection aconteça.

Em JAVA por exemplo onde a tipagem das variáveis é forte, você é obrigado a validar manualmente cada tipo de dado, fazendo com que o script seja seguro e confiável apesar de ser trabalhoso.
No PHP a tipagem é fraca então quando se recebe dados via POST ou GET de um formulário, tudo vira string e isso é péssimo para query.

Muitos programadores se irritam com isso e adotam a postura de tipar tudo a força no PHP para que assim como no java se obtenha algo seguro.
Em grande parte estes programadores abominam a automaticidade com que o
PHP trata as coisas pois muitas vezes acaba abrindo oportunidade para que o script fique lento e redundante.

Porém neste artigo eu venho propor a idéia de remover a possibilidade de injection aproveitando esta automaticidade que o PHP nos fornece visando o desenvolvimento de soluções mais simples e um pouco menos
perjurativa :)

A sugestão mais coerente que já vi até o momento (sem o bind de parâmetros) é fazer o casting das variáveis (Conversão de Tipos). Porém fazer isto a mão em todas as queries é algo bem trabalhoso (Mas não deixa responsabilidades ao MySQL). Então minha proposta é uma função que faça isso de forma automatizada onde o MySQL nos ajuda a combater o injection.

A quem diga que a responsabilidade de remover os injections deve ser toda do PHP mas eu acredito que de forma simples o MySQL possa nos ajudar em uma parcela desta responsabilidade.

É o que veremos na pagina a seguir :)

Comentários:

Mostrando 1 - 8 de 8 comentários
Isso ai Marcos Regis. Não houve confusão. O amigo apenas explicou novamente oque já está no artigo. Basta raciocinar. Se o typecast automatico do PHP fosse inteligente, não haveria a necessidade deste artigo. Como quando se passa algo via GET ou POST o TYPECAST automatico do PHP simplesmente não converte nada e trata tudo como uma string, eu coloquei no artigo uma forma para que o mesmo trate corretamente. Como disse, uma forma diferente de fazer com que o TYPECAST automatico do PHP trabalhe identificando o verdadeiro valor enviado, já que o PHP não é uma liguagem fortemente tipada que exija que seu valor seja declarado antes de utilizar a mesma. Obrigado pela contribuição, links e opnião.

Abraços.
27/12/2011 5:15pm (~13 anos atrás)

Marcos Regis disse:
Ibrahim, creio que você está confundindo os assuntos.
TYPECASTING é a capacidade da linguagem de converter um tipo em outro tipo.
Não confunda com tipagem fraca e tipagem forte.
Como PHP é fracamente tipado, por "default" qualquer valor definido via requisição é sempre uma string. Não entendi isso de identificar corretamente valores vindos de GET e POST. O PHP faz sua parte como deveria.
Tudo que vem por requisição é por padrão "string".
Seu TYPECASTING é automático (implícito) como expliquei no exemplo.
Em linguagens mais fortemente tipadas você precisa indicar qual vai ser o tipo dela antes de usá-la como tal.

Pra quem quer entender melhor do que estamos falando
http://en.wikipedia.org/wiki/Type_conversion
http://php.net/manual/pt_BR/language.types.type-juggling.php



27/12/2011 12:40pm (~13 anos atrás)

Marcos Regis, experimente o typecasting automatico do php em valores enviados por um formulário HTML via $_POST ou $_GET, pra você ver como ele funciona "bem". Veja se por um acaso o var_dump identifica os valores corretamente.

Como comentei posts abaixo, quis mostrar apenas mais uma forma de se fazer. Claro que com PDO fica 100%. Porém com PDO muitos amigos já fizeram artigos e se acha muita coisa no google. Type Cast no PHP não.

Mas agradeço mais uma vez a opnião de todos =)
23/12/2011 11:52pm (~13 anos atrás)

Ok, boa a preocupação com o famoso e polemico injection. Não acompanho o php brasil a anos, vejo que ele está meio 'paradão', legal ver que existem pessoas interessadas em escrever artigos. Parabéns!

Como todos eu concordo, isso deve ser usado para fins didáticos.

Este assunto ainda existe, e muito por ai, e muitos poucos falam sobre a PDO. Por este ser um topico recente em data, lembro aos amigos do PHP brasil que o tempo passou, e o PDO está ai para ser usado.
23/12/2011 2:29pm (~13 anos atrás)

Marcos Regis disse:
PHP tem TypeCasting SIM. E pior o TYPECASTING dele é automático.
Ex.:
$i=12345.00; // float
$i=$i."AA"; // string BB12345.00AA
$i=(int) $i; // integer 12345
O que PHP não tem suporte é o uso de TYPECASTING em assinatura de funções para tipos não escalares (PHP 5).
Conforme todos mencionaram, o artigo foi muito esclarecedor para aqueles que ainda não entendem o porquê deve-se preocupar com esse assunto.
E ainda esclarece que usar funções mysql_* já deveria ter sido abolido da linguagem em detrimento das funções que suporte bind como mysqli e PDO.
PDO inclusive é o padrão de praticamente todos os Frameworks.

22/12/2011 7:22pm (~13 anos atrás)

Hélio disse:
Independente de tudo, a intenção é importante. Mas neste caso, é completamente desnecessário e só deve ser vir pra estudo.
22/12/2011 5:01pm (~13 anos atrás)

Então Diego, na verdade minha idéia foi disponibilizar um Anti-Injection via Type Cast, coisa que o PHP não tem. E se ele não tem, toda e qualquer forma de faze-la será uma "gambiarra" mesmo. Cada um faz/utiliza a forma de Anti-Injection que achar melhor. Em momento algum eu declarei que utilizar Type Cast é melhor que Bind. Apenas dei mais uma opção a quem quiser utilizar. Se você não quer ou não ve a necessidade de usar, não a utilize.

Muito Obrigado por opinar.

Abraços.
21/12/2011 11:25pm (~13 anos atrás)

Diego disse:
Muito boa a sua iniciatava, porém não vi necessidade de criar toda essa gambiarra na função. Quer previnir Injection no PHP? Binda as variáveis antes, simples, funcional no pog.
Utilize o PDO para fazer a persistencia ou se você utiliza algum framework MVC, grande maioria já vem com opção se bindar se não vem, mude de framework.
20/12/2011 8:25am (~13 anos atrás)

Novo Comentário:

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