Quais são as melhores funções de limpeza de entrada do PHP?

161

Estou tentando criar uma função na qual eu possa passar todas as minhas cordas para higienizar. Para que a string que sai dela seja segura para inserção no banco de dados. Mas existem tantas funções de filtragem por aí que não tenho certeza de quais devo usar / precisar.

Por favor, ajude-me a preencher os espaços em branco:

function filterThis($string) {
    $string = mysql_real_escape_string($string);
    $string = htmlentities($string);
    etc...
    return $string;
}
Lauren
fonte
4
para inserção, é bom apenas higienizar contra injeção de SQL usando mysql_real_escape_string. É quando você está usando os dados SELECTed (na saída html ou em uma fórmula / função php) que deve aplicar htmlentities
davidosomething
Consulte stackoverflow.com/questions/60174/… para obter uma resposta específica para limpeza para inserção de banco de dados (fornece um exemplo de DOP, que outras pessoas mencionaram abaixo).
Pat

Respostas:

433

Pare!

Você está cometendo um erro aqui. Ah, não, você escolheu as funções PHP corretas para tornar seus dados um pouco mais seguros. Isso é bom. Seu erro está na ordem das operações , e como e onde usar essas funções.

É importante entender a diferença entre higienizar e validar dados do usuário, escapar dados para armazenamento e escapar dados para apresentação.

Sanitizando e validando dados do usuário

Quando os usuários enviam dados, você precisa garantir que eles tenham fornecido o que você espera.

Sanitização e Filtragem

Por exemplo, se você espera um número, verifique se os dados enviados são um número . Você também pode converter dados do usuário em outros tipos. Tudo o que é enviado é tratado inicialmente como uma string, forçando os dados numéricos conhecidos a serem um número inteiro ou flutuante, tornando a higienização rápida e sem problemas.

E os campos de texto de forma livre e as áreas de texto? Você precisa garantir que não haja nada inesperado nesses campos. Principalmente, você precisa garantir que os campos que não deveriam ter conteúdo HTML não contenham HTML. Existem duas maneiras de lidar com esse problema.

Primeiro, você pode tentar escapar da entrada HTML com htmlspecialchars. Você não deve usar htmlentitiespara neutralizar o HTML, pois ele também executará a codificação de caracteres acentuados e outros que acha que também precisam ser codificados.

Segundo, você pode tentar remover qualquer HTML possível. strip_tagsé rápido e fácil, mas também desleixado. O Purificador de HTML faz um trabalho muito mais completo, removendo todo o HTML e também permitindo uma lista de permissões seletiva de tags e atributos.

As versões modernas do PHP são fornecidas com a extensão de filtro , que fornece uma maneira abrangente de higienizar a entrada do usuário.

Validação

Garantir que os dados enviados estejam livres de conteúdo inesperado é apenas metade do trabalho. Você também precisa tentar garantir que os dados enviados contenham valores com os quais você possa trabalhar.

Se você espera um número entre 1 e 10, precisa verificar esse valor. Se você estiver usando uma dessas novas entradas numéricas da era HTML5 sofisticadas com um girador e etapas, verifique se os dados enviados estão alinhados com a etapa.

Se esses dados vieram do que deveria ser um menu suspenso, verifique se o valor enviado é aquele que apareceu no menu.

E as entradas de texto que atendem a outras necessidades? Por exemplo, as entradas de data devem ser validadas por meio strtotimeda classe DateTime . A data especificada deve estar entre os intervalos esperados. E os endereços de email? A extensão de filtro mencionada anteriormente pode verificar se um endereço está bem formado, embora eu seja fã da biblioteca is_email .

O mesmo vale para todos os outros controles de formulário. Possui botões de opção? Valide com relação à lista. Tem caixas de seleção? Valide com relação à lista. Tem um upload de arquivo? Verifique se o arquivo é do tipo esperado e trate o nome do arquivo como dados não filtrados do usuário.

Todo navegador moderno vem com um conjunto completo de ferramentas de desenvolvedor integradas, o que torna trivial para qualquer um manipular seu formulário. Seu código deve assumir que o usuário removeu completamente todas as restrições do lado do cliente no conteúdo do formulário !

Escapando Dados para Armazenamento

Agora que você certificou-se de que seus dados estão no formato esperado e contém apenas valores esperados, é necessário se preocupar em manter esses dados no armazenamento.

Todo mecanismo de armazenamento de dados possui uma maneira específica de garantir que os dados sejam escapados e codificados corretamente. Se você estiver criando SQL, a maneira aceita de passar dados nas consultas é através de instruções preparadas com espaços reservados .

Uma das melhores maneiras de trabalhar com a maioria dos bancos de dados SQL no PHP é a extensão PDO . Ele segue o padrão comum de preparar uma instrução , vinculando variáveis ​​à instrução e enviando a instrução e as variáveis ​​ao servidor . Se você não trabalhou com o PDO antes, aqui está um bom tutorial orientado ao MySQL .

Alguns bancos de dados SQL têm suas próprias extensões especializadas em PHP, incluindo SQL Server , PostgreSQL e SQLite 3 . Cada uma dessas extensões preparou o suporte à instrução que opera da mesma maneira que a DOP. Às vezes, pode ser necessário usar essas extensões em vez do PDO para oferecer suporte a recursos ou comportamentos não padrão.

O MySQL também possui suas próprias extensões PHP. Dois deles, de fato. Você só quer usar o chamado mysqli . A antiga extensão "mysql" foi descontinuada e não é segura ou sã para uso na era moderna.

Eu pessoalmente não sou fã de mysqli. A maneira como ele executa a ligação variável nas instruções preparadas é inflexível e pode ser uma tarefa difícil de usar. Em caso de dúvida, use o DOP.

Se você não estiver usando um banco de dados SQL para armazenar seus dados, verifique a documentação da interface do banco de dados que está usando para determinar como passar dados com segurança por ele.

Quando possível, verifique se o seu banco de dados armazena seus dados em um formato apropriado. Armazene números em campos numéricos. Armazene as datas nos campos de data. Armazene dinheiro em um campo decimal, não em um campo de ponto flutuante. Revise a documentação fornecida pelo seu banco de dados sobre como armazenar corretamente diferentes tipos de dados.

Escapando dados para apresentação

Toda vez que você mostra dados aos usuários, você deve garantir que os dados sejam escapados com segurança, a menos que você saiba que não deve ser escapado.

Ao emitir HTML, você quase sempre deve passar os dados originalmente fornecidos pelo usuário htmlspecialchars. Na verdade, a única vez que você não deve fazer isso é quando você sabe que o usuário forneceu HTML, e que você sabe que ele já foi higienizado-lo usando uma whitelist.

Às vezes você precisa gerar algum Javascript usando PHP. Javascript não tem as mesmas regras de escape que HTML! É uma maneira segura de fornecer valores fornecidos pelo usuário ao Javascript via PHP json_encode.

E mais

Existem muito mais nuances na validação de dados.

Por exemplo, a codificação do conjunto de caracteres pode ser uma grande armadilha . Seu aplicativo deve seguir as práticas descritas em " UTF-8 o tempo todo ". Existem ataques hipotéticos que podem ocorrer quando você trata os dados da string como o conjunto de caracteres errado.

Mencionei anteriormente as ferramentas de depuração do navegador. Essas ferramentas também podem ser usadas para manipular dados de cookies. Os cookies devem ser tratados como entrada não confiável do usuário .

A validação e escape de dados são apenas um aspecto da segurança de aplicativos da web. Você deve conhecer as metodologias de ataque de aplicativos da Web para poder criar defesas contra elas.

Charles
fonte
E ao especificá-lo, verifique se ele está na lista de codificações suportadas.
Charles
3
E não use htmlentities, substitua-o por htmlspecialchars com o objetivo de substituir apenas <>, nem todos os caracteres de sua entidade
Seu senso comum
6
Apenas não ligue htmlspecialcharsduas vezes, porque ele fala disso na parte "Quando os usuários enviam parte dos dados" e na parte "Ao exibir os dados".
Savageman
2
Votado. A resposta mais útil que li de muitas perguntas e respostas sobre injeção de SQL.
akinuri
Absolutamente uma resposta de qualidade com muitas explicações e links para futuros usuários explorarem mais opções. Também tenho uma cópia de mim ...
James Walker
32

A sanitização mais eficaz para impedir a injeção de SQL é a parametrização usando PDO . Usando consultas parametrizadas, a consulta é separada dos dados, para remover a ameaça de injeção SQL de primeira ordem.

Em termos de remoção de HTML, strip_tagsé provavelmente a melhor ideia para remover HTML, pois apenas removerá tudo. htmlentitiesfaz o que parece, e isso também funciona. Se você precisar analisar qual HTML permitir (ou seja, você deseja permitir algumas tags), use um analisador existente existente, como o HTML Purifier

Derek H
fonte
2
Ah, cara, eu escrevi aquela parede gigante de texto só porque eu não vi ninguém mencionar HTML Purifier, e aqui você me venceu por 40 minutos. ;) #
Charles
3
Você não deveria apenas retirar o HTML na saída? Na IMO, você nunca deve alterar os dados de entrada - você nunca sabe quando precisará deles
Joe Phillips
11

Entrada do banco de dados - Como evitar a injeção de SQL

  1. Verifique se os dados do tipo número inteiro, por exemplo, são válidos, garantindo que realmente sejam números inteiros
    • No caso de não-strings, você precisa garantir que os dados sejam do tipo correto
    • No caso de strings, você precisa garantir que a string esteja entre aspas na consulta (obviamente, caso contrário, nem funcionaria)
  2. Digite o valor no banco de dados, evitando a injeção de SQL (mysql_real_escape_string ou consultas parametrizadas)
  3. Ao recuperar o valor do banco de dados, evite ataques de Cross Site Scripting, garantindo que o HTML não possa ser injetado na página (htmlspecialchars)

Você precisa escapar da entrada do usuário antes de inseri-la ou atualizá-la no banco de dados. Aqui está uma maneira antiga de fazer isso. Você gostaria de usar consultas parametrizadas agora (provavelmente da classe PDO).

$mysql['username'] = mysql_real_escape_string($clean['username']);
$sql = "SELECT * FROM userlist WHERE username = '{$mysql['username']}'";
$result = mysql_query($sql);

Saída do banco de dados - Como impedir o XSS (Cross Site Scripting)

Use htmlspecialchars()somente ao emitir dados do banco de dados. O mesmo se aplica ao purificador de HTML. Exemplo:

$html['username'] = htmlspecialchars($clean['username'])

E finalmente ... o que você solicitou

Devo salientar que, se você usar objetos DOP com consultas parametrizadas (a maneira correta de fazer isso), não haverá realmente uma maneira fácil de conseguir isso facilmente. Mas se você usar o antigo modo 'mysql', é isso que você precisa.

function filterThis($string) {
    return mysql_real_escape_string($string);
}
Joe Phillips
fonte
5

Meus 5 centavos.

Ninguém aqui entende como mysql_real_escape_stringfunciona. Esta função não filtra ou "desinfecta" nada.
Portanto, você não pode usar esta função como um filtro universal que o salvará da injeção.
Você pode usá-lo apenas quando entender como funciona e onde aplicável.

Eu tenho a resposta para a pergunta muito semelhante que já escrevi: No PHP, ao enviar seqüências de caracteres para o banco de dados, devo cuidar de caracteres ilegais usando htmlspecialchars () ou usar uma expressão regular?
Clique para obter a explicação completa da segurança do banco de dados.

Quanto às htmlentities - Charles está certo dizendo para você separar essas funções.
Imagine que você irá inserir dados, gerados por admin, com permissão para postar HTML. sua função irá estragá-lo.

Embora eu recomendaria contra htmlentities. Esta função ficou obsoleta há muito tempo. Se você deseja substituir apenas <, >e "caracteres em prol da segurança do HTML - use a função que foi desenvolvida intencionalmente para esse fim - uma htmlspecialchars () .

Seu senso comum
fonte
1
mysql_real_escape_stringescapa os caracteres necessários dentro de uma string. Não é estritamente filtrante ou higienizante, mas incluir uma string entre aspas também não é (e todo mundo faz isso, eu praticamente nunca vi uma pergunta sobre isso). Então, nada é higienizado quando escrevemos SQL? Claro que não. O que impede a injeção de SQL é o uso de mysql_real_escape_string. Também as aspas anexas, mas todo mundo faz isso e, se você testar o que faz, você acaba com um erro de sintaxe SQL com essa omissão. A parte realmente perigosa é tratada mysql_real_escape_string.
Savageman
@ Savageman desculpe amigo, você não entende nada. Você não entende como o mysql_real_escape_string funciona. Esses "caracteres necessários" SÃO aspas. Nem esta função nem as aspas isolam qualquer coisa. Essas duas coisas funcionam apenas em conjunto . Tornar a string de consulta apenas sintaticamente correta, não "protegida contra injeção". E que erro de sintaxe eu receberia apenas WHERE id = 1? ;)
Seu senso comum
Tente WHERE my_field = two words(sem aspas) obter o erro de sintaxe. Seu exemplo é ruim porque não precisa de aspas nem de escape, apenas uma verificação numérica. Também não disse que as citações eram inúteis. Eu disse que todo mundo os usa, então essa não é a fonte de problemas relacionados à injeção de SQL.
Savageman
1
@ Savageman, então eu disse: Você pode usá-lo apenas quando entender como funciona e onde aplicável. Você acabou de admitir que mysql_real_escape_string não é aplicável em qualquer lugar. Quanto a everyone use themvocê, pode verificar os códigos aqui no SO. Muitas pessoas não usam aspas com números. Vai saber. Por favor, lembre-se de que não discuto aqui o que você disse e o que não disse. Apenas explico as regras básicas de segurança do banco de dados. É melhor você aprender em vez de argumentar vazio. Ninguém mencionou citações ou transmissão aqui, mas m_r_e_s apenas como se fosse mágica. O que estou falando
Seu senso comum
1
um, assim como @Charles. Como novato, a interação com o banco de dados ... tornando as coisas seguras para entrada e exibição, Caracteres especiais, problemas de injeção, tem sido uma curva de aprendizado muito acentuada. Ler seu post e seu (bem como suas outras respostas PHP para outras perguntas, me ajudou muito Tx para todas as suas entradas..
James Walker
2

Para inserção de banco de dados, tudo que você precisa é mysql_real_escape_string(ou usar consultas parametrizadas). Você geralmente não deseja alterar os dados antes de salvá-los, o que aconteceria se você o usasse htmlentities. Isso levaria a uma confusão confusa mais tarde, quando você a executaria htmlentitiesnovamente para exibi-la em algum lugar de uma página da web.

Use htmlentitiesquando você estiver exibindo os dados em uma página da Web em algum lugar.

De certa forma, se você estiver enviando dados enviados para algum lugar de um email, como por exemplo um formulário de contato, retire novas linhas de qualquer dado que será usado no cabeçalho (como: nome e endereço de email, sub-seção, etc. )

$input = preg_replace('/\s+/', ' ', $input);

Se você não fizer isso, é apenas uma questão de tempo até que os bots de spam encontrem seu formulário e o abusem, eu aprendi da maneira mais difícil.

Roubar
fonte
2

Depende do tipo de dados que você está usando. A melhor maneira geral de usar seria mysqli_real_escape_string, mas, por exemplo, você sabe que não haverá conteúdo HTML, usar strip_tags adicionará segurança extra.

Você também pode remover caracteres que você sabe que não devem ser permitidos.

Aaron Harun
fonte
1

Eu sempre recomendo usar um pequeno pacote de validação como GUMP: https://github.com/Wixel/GUMP

Crie todas as suas funções básicas em uma biblioteca como essa e é quase impossível esquecer o saneamento. "mysql_real_escape_string" não é a melhor alternativa para uma boa filtragem (como "Your Common Sense" explicado) - e se você esquecer de usá-la apenas uma vez, todo o seu sistema será atacável através de injeções e outros ataques desagradáveis.

Simon Schneider
fonte
1

Para todos aqueles aqui falando e confiando no mysql_real_escape_string, você precisa notar que essa função foi descontinuada no PHP5 e não existe mais no PHP7.

IMHO, a melhor maneira de realizar essa tarefa é usar consultas parametrizadas através do uso do PDO para interagir com o banco de dados. Verifique isto: https://phpdelusions.net/pdo_examples/select

Sempre use filtros para processar a entrada do usuário. Veja http://php.net/manual/es/function.filter-input.php

Kuntur
fonte
Na verdade, isso não responde à pergunta. Considere modificar sua resposta para incluir uma solução.
kris
Espero que você goste!
precisa
Eu faço. Boa resposta!
kris
Sugiro que você observe que no PHP 7 mysqli_real_escape_string()está disponível.
28418 Chris
Olá, Chris, as soluções expostas aqui fizeram referência ao mysql_real_escape_string, notei que a partir de agora lemos que ele não existe mais no PHP7 e propus uma alternativa usando DOP (e filtros) e não o mysqli. Sinta-se livre para adicionar uma nota explicando uma solução usando o que você sugere. Atenciosamente
Kuntur
0

Você usa mysql_real_escape_string () em código semelhante ao seguinte.

$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
  mysql_real_escape_string($user),
  mysql_real_escape_string($password)
);

Como a documentação diz, seu objetivo é escapar caracteres especiais na cadeia passada como argumento, levando em consideração o conjunto de caracteres atual da conexão, para que seja seguro colocá-lo em um mysql_query () . A documentação também adiciona:

Se for necessário inserir dados binários, esta função deve ser usada.

htmlentities () é usado para converter alguns caracteres em entidades, quando você uma string no conteúdo HTML.

kiamlaluno
fonte
0

Esta é uma das maneiras pelas quais estou praticando atualmente,

  1. Implante o csrf e o salt tempt token junto com a solicitação a ser feita pelo usuário e valide-os todos juntos a partir da solicitação. Consulte aqui
  2. garanta não depender muito dos cookies do lado do cliente e pratique o uso de sessões do lado do servidor
  3. quando houver dados de análise, aceite apenas o tipo de dados e o método de transferência (como POST e GET)
  4. Certifique-se de usar o SSL para seu webApp / App
  5. Certifique-se de também gerar solicitação de sessão com base no tempo para restringir intencionalmente a solicitação de spam.
  6. Quando os dados são analisados ​​no servidor, verifique se a solicitação deve ser feita no método que você deseja, como json, html e etc ... e prossiga
  7. escape todos os atributos ilegais da entrada usando o tipo de escape ... como realescapestring.
  8. depois disso, verifique apenas o formato limpo do tipo de dados que você deseja do usuário.
    Exemplo:
    - E-mail: verifique se a entrada está no formato de e-mail válido
    - texto / sequência: verifique apenas se a entrada é apenas no formato de texto (sequência)
    - número: verifique se apenas o formato numérico é permitido.
    - etc. Pelase consulte a biblioteca de validação de entrada php no portal php
    - Depois de validado, continue usando a instrução SQL / PDO preparada.
    - Uma vez feito, certifique-se de sair e encerrar a conexão
    - Não se esqueça de limpar o valor de saída assim que terminar.

Isso é tudo o que acredito ser suficiente para um segundo básico. Deve impedir todos os principais ataques do hacker.

Para segurança no servidor, convém definir no apache / htaccess a limitação de acessos e a prevenção de robôs e também a prevenção de roteamento.

Você pode aprender e obter uma cópia do segundo no nível htaccess apache sec (rpactices comuns)

Ahmad Anuar
fonte
0
function sanitize($string,$dbmin,$dbmax){
$string = preg_replace('#[^a-z0-9]#i', '', $string); //useful for strict cleanse, alphanumeric here
$string = mysqli_real_escape_string($con, $string); //get ready for db
if(strlen($string) > $dbmax || strlen($string) < $dbmin){
    echo "reject_this"; exit();
    }
return $string;
}
stkmedia
fonte
0

que tal isso

$string = htmlspecialchars(strip_tags($_POST['example']));

ou isto

$string = htmlentities($_POST['example'], ENT_QUOTES, 'UTF-8');
jerryurenaa
fonte