Quando se trata de consultas de banco de dados, sempre tente usar consultas parametrizadas preparadas. As bibliotecas mysqli
e PDO
suportam isso. Isso é infinitamente mais seguro do que usar funções de escape, como mysql_real_escape_string
.
Sim, mysql_real_escape_string
é efetivamente apenas uma função de escape de string. Não é uma bala mágica. Tudo o que fará é escapar caracteres perigosos para que possam ser usados com segurança em uma única string de consulta. No entanto, se você não higienizar suas entradas de antemão, ficará vulnerável a certos vetores de ataque.
Imagine o seguinte SQL:
$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);
Você deve ser capaz de ver que isso é vulnerável a exploração.
Imagine que o id
parâmetro contenha o vetor de ataque comum:
1 OR 1=1
Não há caracteres arriscados para codificar, então ele passará direto pelo filtro de escape. Deixando-nos:
SELECT fields FROM table WHERE id= 1 OR 1=1
Que é um adorável vetor de injeção de SQL e permitiria ao invasor retornar todas as linhas. Ou
1 or is_admin=1 order by id limit 1
que produz
SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1
O que permite que o invasor retorne os detalhes do primeiro administrador neste exemplo completamente fictício.
Embora essas funções sejam úteis, elas devem ser usadas com cuidado. Você precisa garantir que todas as entradas da web sejam validadas em algum grau. Neste caso, vemos que podemos ser explorados porque não verificamos se uma variável que estávamos usando como um número era realmente numérica. Em PHP, você deve usar amplamente um conjunto de funções para verificar se as entradas são inteiros, flutuantes, alfanuméricos, etc. Mas quando se trata de SQL, preste atenção ao valor da instrução preparada. O código acima teria sido seguro se fosse uma instrução preparada, pois as funções do banco de dados saberiam que 1 OR 1=1
não é um literal válido.
Quanto a htmlspecialchars()
. Esse é um campo minado por si só.
Há um problema real no PHP em que ele tem uma seleção inteira de diferentes funções de escape relacionadas ao html, e nenhuma orientação clara sobre exatamente quais funções fazem o quê.
Em primeiro lugar, se você estiver dentro de uma tag HTML, você está realmente com problemas. Olhe para
echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';
Já estamos dentro de uma tag HTML, então não precisamos <ou> fazer nada perigoso. Nosso vetor de ataque poderia ser apenasjavascript:alert(document.cookie)
Agora o HTML resultante parece
<img src= "javascript:alert(document.cookie)" />
O ataque é direto.
Fica pior. Por quê? porque htmlspecialchars
(quando chamado dessa forma) codifica apenas aspas duplas e não simples. Então, se tivéssemos
echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";
Nosso atacante malvado agora pode injetar parâmetros totalmente novos
pic.png' onclick='location.href=xxx' onmouseover='...
nos dá
<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />
Nesses casos, não há fórmula mágica, você apenas precisa santificar a entrada você mesmo. Se você tentar filtrar caracteres ruins, certamente falhará. Use uma abordagem de lista de permissões e deixe passar apenas os caracteres que são bons. Veja a folha de dicas do XSS para exemplos de como diversos vetores podem ser
Mesmo se você usar htmlspecialchars($string)
fora das tags HTML, ainda estará vulnerável a vetores de ataque de conjuntos de caracteres multibyte.
O mais eficaz que você pode ser é usar uma combinação de mb_convert_encoding e htmlentities como segue.
$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
$str = htmlentities($str, ENT_QUOTES, 'UTF-8');
Mesmo isso deixa o IE6 vulnerável, devido à maneira como ele lida com UTF. No entanto, você pode recorrer a uma codificação mais limitada, como ISO-8859-1, até que o uso do IE6 diminua.
Para um estudo mais aprofundado dos problemas multibyte, consulte https://stackoverflow.com/a/12118602/1820
$result = "SELECT fields FROM table WHERE id = '".mysql_real_escape_string($_POST['id'])."'";
2. No segundo caso (atributo contendo URL), não há uso parahtmlspecialchars
; nesses casos, você deve codificar a entrada usando um esquema de codificação de URL, por exemplo, usandorawurlencode
. Dessa forma, um usuário não pode inserirjavascript:
et al.Take a whitelist approach and only let through the chars which are good.
Uma lista negra sempre deixará escapar algo. +1Além da excelente resposta da Cheekysoft:
Não existe realmente uma solução mágica para prevenir a injeção de HTML (por exemplo, cross site scripting), mas você pode conseguir isso mais facilmente se estiver usando uma biblioteca ou sistema de modelos para gerar HTML. Leia a documentação para saber como escapar das coisas apropriadamente.
Em HTML, as coisas precisam ter um escape diferente, dependendo do contexto. Isso é especialmente verdadeiro para strings sendo colocadas em Javascript.
fonte
Eu definitivamente concordaria com as postagens acima, mas tenho uma pequena coisa a acrescentar em resposta à resposta da Cheekysoft, especificamente:
Eu codifiquei uma pequena função rápida que coloquei em minha classe de banco de dados que eliminará qualquer coisa que não seja um número. Ele usa preg_replace, então há uma função um pouco mais otimizada, mas funciona em uma pitada ...
Então, em vez de usar
eu usaria
e executaria com segurança a consulta
Claro, isso apenas o impediu de exibir a linha correta, mas não acho que seja um grande problema para quem está tentando injetar sql em seu site;)
fonte
return preg_match('/^[0-9]+$/',$input) ? $input : 0;
Uma peça importante desse quebra-cabeça são os contextos. Alguém enviando "1 OR 1 = 1" como o ID não é um problema se você citar todos os argumentos em sua consulta:
O que resulta em:
o que é ineficaz. Já que você está escapando da string, a entrada não pode sair do contexto da string. Eu testei isso até a versão 5.0.45 do MySQL, e usar um contexto de string para uma coluna inteira não causa problemas.
fonte
Funciona bem, ainda melhor em sistemas de 64 bits. No entanto, tome cuidado com as limitações de seu sistema ao lidar com grandes números, mas para IDs de banco de dados isso funciona bem 99% das vezes.
Você deve usar uma única função / método para limpar seus valores também. Mesmo que esta função seja apenas um wrapper para mysql_real_escape_string (). Por quê? Porque um dia, quando uma exploração do seu método preferido de limpeza de dados for encontrada, você só terá que atualizá-la em um local, em vez de localizar e substituir em todo o sistema.
fonte
por que, oh POR QUE, você não incluiria aspas em torno da entrada do usuário em sua instrução sql? parece muito bobo não o fazer! incluir aspas em sua instrução sql tornaria "1 ou 1 = 1" uma tentativa infrutífera, não?
então agora, você dirá, "e se o usuário incluir uma aspa (ou aspas duplas) na entrada?"
bem, solução fácil para isso: apenas remova as citações inseridas pelo usuário. por exemplo:
input =~ s/'//g;
. agora, parece-me de qualquer maneira, que a entrada do usuário seria protegida ...fonte