Validação e Higienização de entradas em PHP
Algo que já falamos anteriormente é que nunca podemos confiar na entradas fornecidas pelo usuário. Seja por descuido ou mesmo maldade um dado de entrada errada podem causar sérios problemas, então realizar a higienização e validação das entradas é a primeira linha de segurança de uma aplicação.
A validação é o processo de verificar se os dados cumprem certos requisitos previamente definidos, por exemplo se um valor é um e-mail, se o valor é numérico, etc. O processo de validação não deve alterar o dado.
A higienização é o processo de limpar os dados, ou seja, retirar todos os trechos indesejados. Por exemplo, retirar todas as letras de uma entrada que deveria ser numérica. A higienização altera os dados.
O PHP possui uma extensão para validar e higienizar os dados que é bastante interessante, ela é disponível através de diversas funções onde as mais utilizadas são filter_var()
e filter_input()
.
A função filter_var()
recebe como argumento um valor e um identificador de filtro que pode ser um filtro de validação ou de higienação e retorna o valor processado, se o filtro falhar(caso seja um filtro de validação) retornará false.
$comentario = "<h1>Olá! Como \"vai\" você?</h1>";
echo $comentario . PHP_EOL;
$comentarioHigienizado = filter_var($comentario, FILTER_SANITIZE_STRING);
echo $comentarioHigienizado . PHP_EOL;
// saida
// <h1>Olá! Como "vai" você?</h1>
// Olá! Como "vai" você?
A função filter_input()
processa os valores de uma determinada entrada externa, ou seja, as variáveis $_GET
, $_POST
, etc, e deve ser informada como primeiro argumento. Podemos utilizar:
- INPUT_GET: pega do array associativo de variáveis passadas para o script atual via o método HTTP GET, ou seja, os valores do
$_GET
- INPUT_POST: pega do array associativo de variáveis passados para o script atual via método HTTP POST , ou seja, os valores do
$_POST
. - INPUT_COOKIE: pega do array associativo de variáveis passadas para o atual script via HTTP Cookies, ou seja, os valores do
$_COOKIE
. - INPUT_SERVER: pega do array associativo contendo referências para todas as variáveis que estão atualmente definidas no escopo global do script, ou seja, do
$_SERVER
. - INPUT_ENV: pega do array associativo de variáveis passadas para o script atual via o método do ambiente, ou seja, do
$_ENV
.
Como podemos ver todas as entradas são na verdade arrays associativos, então também devemos passar qual chave dele queremos filtrar, passamos ela como segundo argumento para o o método filter_input
e como terceiro o identificador do filtro desejado.
echo $_GET['nome'] . PHP_EOL;
$nomeHigienizado = filter_input(INPUT_GET, 'nome', FILTER_SANITIZE_STRING);
echo $nomeHigienizado . PHP_EOL;
// acessando index.php?nome=<a%20onclick="alert()">Rodrigo</a>
// saida:
// <a onclick="alert()">Rodrigo</a>
// Rodrigo
Filtros
Para utilizar as funções como vimos devemos estipular qual filtro queremos utilizar, temos duas categorias, de validação e de higienização. Tanto um quanto o outro podem receber flags e opções para configurar o comportamento conforme necessário.
As flags devem ser informadas utilizando sua constante, se utilizarmos mais de uma flag basta separá-las com o “|” (ex. FILTER_NULL_ON_FAILURE | FILTER_FLAG_ALLOW_HEX
). Se formos utilizar flags em combinação com opções devemos colocá-las em um array associativo onde sua chave será flags
.
As opções são informadas através de um array associativo com a chave options
, e com seu valor sendo um array associativo contendo as configurações válidas para um determinado filtro.
No exemplo abaixo configuramos um filtro FILTER_VALIDATE_INT
, com a flag FILTER_NULL_ON_FAILURE
, que irá retornar null se falhar a validação(em vez de false) e com as opções de valor mínimo aceito(min_range
) de 1 e máximo(max_range
) de 10.
$options = [
'flags' => FILTER_NULL_ON_FAILURE,
'options' => [
'min_range' => 1,
'max_range' => 10
]
];
A flag FILTER_NULL_ON_FAILURE
é uma flag comum a todos os filtros de validação, que muda o retorno da função de false para null caso ela falhe.
Filtros de validação
FILTER_VALIDATE_BOOLEAN
Retorna true
para 1, true, on e yes. Retorna false
caso contrário.
Se FILTER_NULL_ON_FAILURE
está definido, false
é retornado apenas para “0”, “false”, “off”, “no”, e “”, e null
é retornado para todos os valores não booleanos.
$value1 = 1;
$value2 = 0;
$value3 = 'a';
$valueValidate1 = filter_var($value1, FILTER_VALIDATE_BOOLEAN);
$valueValidate2 = filter_var($value2, FILTER_VALIDATE_BOOLEAN);
$valueValidate3 = filter_var($value3, FILTER_VALIDATE_BOOLEAN);
$valueValidate3_2 = filter_var($value3, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
var_dump($valueValidate1);
var_dump($valueValidate2);
var_dump($valueValidate3);
var_dump($valueValidate3_2);
// bool(true)
// bool(false)
// bool(false)
// NULL
FILTER_VALIDATE_EMAIL
Valida se o valor é um endereço de e-mail válido.
$email1 = "rodrigo@botecodigital.dev.br";
$email2 = "rodrigo#botecodigital.dev.br";
$email3 = "rodrigoção@botecodigital.dev.br";
$emailValidate1 = filter_var($email1, FILTER_VALIDATE_EMAIL);
$emailValidate2 = filter_var($email2, FILTER_VALIDATE_EMAIL);
$emailValidate3 = filter_var($email3, FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE);
var_dump($emailValidate1);
var_dump($emailValidate2);
var_dump($emailValidate3);
// saida:
// string(28) "rodrigo@botecodigital.dev.br"
// bool(false)
// string(33) "rodrigoção@botecodigital.dev.br"
FILTER_VALIDATE_FLOAT
Valida valor como float, e converte para float em caso de sucesso.
Se FILTER_FLAG_ALLOW_THOUSAND
for utilizada aceita virgula como separador de milhar.
Também é possível passar um array com a chave options
que recebe também um array associativo, com um intervalo de valores válido, como chaves deste array temos min_range
(valor mínimo aceito) e max_range
(valor máximo aceito).
$value1 = '10.0';
$value2 = 'a10.0';
$value3 = '0.10.050';
$value4 = '1,010.5';
$value5 = '200';
$valueValidate1 = filter_var($value1, FILTER_VALIDATE_FLOAT);
$valueValidate2 = filter_var($value2, FILTER_VALIDATE_FLOAT);
$valueValidate3 = filter_var($value3, FILTER_VALIDATE_FLOAT);
$valueValidate4 = filter_var($value4, FILTER_VALIDATE_FLOAT);
$valueValidate4_2 = filter_var($value4, FILTER_VALIDATE_FLOAT,FILTER_FLAG_ALLOW_THOUSAND);
$valueValidate5 = filter_var($value5, FILTER_VALIDATE_FLOAT, [
'options' => [
'min_range' => 50,
'max_range'=>100]
]);
var_dump($valueValidate1);
var_dump($valueValidate2);
var_dump($valueValidate3);
var_dump($valueValidate4);
var_dump($valueValidate4_2);
var_dump($valueValidate5);
// saida
// float(10)
// bool(false)
// bool(false)
// bool(false)
// float(1010.5)
// bool(false)
FILTER_VALIDATE_INT
Valida o valor como inteiro, opcionalmente do intervalo especificado e converte em int em caso de sucesso.
As flags FILTER_FLAG_ALLOW_HEX
e FILTER_FLAG_ALLOW_OCTAL
servem para aceitar valores hexadecimais e octais respectivamente.
Também é possível passar um array com a chave options
que recebe também um array associativo, com um intervalo de valores válido, como chaves deste array temos min_range
(valor mínimo aceito) e max_range
(valor máximo aceito).
$value1 = '10.0';
$value2 = 'a10.0';
$value3 = '10';
$value4 = 010;
$value5 = 0x10;
$value6 = '150';
$valueValidate1 = filter_var($value1, FILTER_VALIDATE_INT);
$valueValidate2 = filter_var($value2, FILTER_VALIDATE_INT);
$valueValidate3 = filter_var($value3, FILTER_VALIDATE_INT);
$valueValidate4 = filter_var($value4, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_OCTAL);
$valueValidate5 = filter_var($value5, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
$valueValidate6 = filter_var($value6, FILTER_VALIDATE_FLOAT, [
'options' => [
'min_range' => 50,
'max_range'=> 100]
]);
var_dump($valueValidate1);
var_dump($valueValidate2);
var_dump($valueValidate3);
var_dump($valueValidate4);
var_dump($valueValidate5);
var_dump($valueValidate6);
// saida
// bool(false)
// bool(false)
// int(10)
// int(8)
// int(16)
// bool(false)
FILTER_VALIDATE_IP
Valida o valor como endereço IP, opcionalmente é possível restringir a IPs apenas IPv4 ou IPv6 ou de não pertencer a intervalos privados ou reservados.
As flags FILTER_FLAG_IPV4
e FILTER_FLAG_IPV6
restringem a validação a apenas ips nos formatos ipv4 e ipv6 respectivamente. A flag FILTER_FLAG_NO_PRIV_RANGE
restringe a validação a somente faixas de ip que não sejam reservadas a redes privadas(normalmente 10.0.0.0/8, 172.16.0.0/12 e 192.168.0.0/16). E FILTER_FLAG_NO_RES_RANGE
a intervalos restritos.
$ip1 = '192.168.0.3';
$ip2 = '192.168.887.3';
$ip3 = '192.168.0.3';
$ip4 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334';
$ipValidate1 = filter_var($ip1, FILTER_VALIDATE_IP);
$ipValidate2 = filter_var($ip2, FILTER_VALIDATE_IP);
$ipValidate3 = filter_var($ip3, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
$ipValidate3_2 = filter_var($ip3, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
$ipValidate4 = filter_var($ip4, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
$ipValidate5 = filter_var($ip3, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE);
var_dump($ipValidate1);
var_dump($ipValidate2);
var_dump($ipValidate3);
var_dump($ipValidate3_2);
var_dump($ipValidate4);
var_dump($ipValidate5);
// saida
// string(11) "192.168.0.3"
// bool(false)
// bool(false)
// string(11) "192.168.0.3"
// string(39) "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
// string(14) "179.216.84.189"
FILTER_VALIDATE_MAC
Valida o valor como endereço MAC.
$mac1 = 'F6-13-56-1B-3D-67';
$mac2 = 'F6-13-56-1B-3D-xx';
$macValidate1 = filter_var($mac1, FILTER_VALIDATE_MAC);
$macValidate2 = filter_var($mac2, FILTER_VALIDATE_MAC);
var_dump($macValidate1);
var_dump($macValidate2);
// saida
// string(17) "F6-13-56-1B-3D-67"
// bool(false)
FILTER_VALIDATE_REGEXP
Valida o valor contra regexp
, um Perl-compatible expressão regular.
Este filtro tem como option um regexp ao qual será utilizado para validar o valor. Para maiores detalhes de como construir um regex veja nosso post de introdução a elas.
$value1 = "ETE-2341";
$value2 = "ETE-2a4b";
$value3 = "ETE-234121";
$valueValidate1 = filter_var($value1, FILTER_VALIDATE_REGEXP, [ 'options' => [ 'regexp' => '/^[A-Z]{3}-[0-9]{4}$/']] );
$valueValidate2 = filter_var($value2, FILTER_VALIDATE_REGEXP, [ 'options' => [ 'regexp' => '/^[A-Z]{3}-[0-9]{4}$/']] );
$valueValidate3 = filter_var($value3, FILTER_VALIDATE_REGEXP, [ 'options' => [ 'regexp' => '/^[A-Z]{3}-[0-9]{4}$/']] );
var_dump($valueValidate1);
var_dump($valueValidate2);
var_dump($valueValidate3);
FILTER_VALIDATE_URL
Valida o valor como URL (de acordo com » http://www.faqs.org/rfcs/rfc2396), opcionalmente com componentes requeridos. Cuidado, uma URL válido pode não especificar o protocolo HTTP http://
Assim, a validação adicional pode ser necessária para determinar a URL utiliza um protocolo esperado, e.g. ssh://
ou mailto:
. Note que a função só encontrará URLs ASCII válidas; nomes de domínio internacionalizados (contendo caracteres não ASCII) vai falhar.
As flags FILTER_FLAG_PATH_REQUIRED
e FILTER_FLAG_QUERY_REQUIRED
restringem a validação a urls que tenham um caminho após o dominio e que tenham query params respectivamente.
$url1 = "https://botecodigital.dev.br";
$url2 = "botecodigital";
$url3 = "https://botecodigital.dev.br";
$url4 = "https://botecodigital.dev.br/teste";
$url5 = "https://botecodigital.dev.br/teste?nome=rodrigo";
$urlValidade1 = filter_var($url1, FILTER_VALIDATE_URL);
$urlValidade2 = filter_var($url2, FILTER_VALIDATE_URL);
$urlValidade3 = filter_var($url3, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED);
$urlValidade4 = filter_var($url4, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED);
$urlValidade5 = filter_var($url5, FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED);
var_dump($urlValidade1);
var_dump($urlValidade2);
var_dump($urlValidade3);
var_dump($urlValidade4);
var_dump($urlValidade5);
// saida
// string(27) "https://botecodigital.dev.br"
// bool(false)
// bool(false)
// string(33) "https://botecodigital.dev.br/teste"
// string(46) "https://botecodigital.dev.br/teste?nome=rodrigo"
Filtros de higienização
FILTER_SANITIZE_EMAIL
Remove todos os caracteres, exceto letras, dígitos e !#$%&'*+-=?^_`{|}~@.[]
.
$email1 = "rodrigo@botecodigital.dev.br";
$email2 = " (/rodrigo@botecodigital.dev.br)";
$emailSanitize1 = filter_var($email1, FILTER_SANITIZE_EMAIL);
$emailSanitize2 = filter_var($email2, FILTER_SANITIZE_EMAIL);
var_dump($emailSanitize1);
var_dump($emailSanitize2);
// saida
// string(28) "rodrigo@botecodigital.dev.br"
// string(28) "rodrigo@botecodigital.dev.br"
FILTER_SANITIZE_ENCODED
Este filtro remove ou codifica caracteres especiais. Funciona como a função urlencode()
Este filtro também aceita as seguintes flags:
FILTER_FLAG_STRIP_LOW
: Remove os caracteres com valor ASCII < 32FILTER_FLAG_STRIP_HIGH
: Remove os caracteres com valor ASCII > 127FILTER_FLAG_ENCODE_LOW
: Codifica os caracteres com valor ASCII < 32FILTER_FLAG_ENCODE_HIGH
: Codifica os caracteres com valor ASCII > 127FILTER_FLAG_STRIP_BACKTICK
: Remove crase dos caracteres.
$url1 = "olá mundo";
$url2 = "\tteste";
$urlSanitize1 = filter_var($url1, FILTER_SANITIZE_ENCODED);
$urlSanitize2 = filter_var($url2, FILTER_SANITIZE_ENCODED, FILTER_FLAG_STRIP_LOW) ;
var_dump($urlSanitize1);
var_dump($urlSanitize2);
// saida
// string(16) "ol%C3%A1%20mundo"
// string(5) "teste"
FILTER_SANITIZE_ADD_SLASHES
Aplica addslashes().
$url1 = "olá \"mundo\"!";
$urlSanitize1 = filter_var($url1, FILTER_SANITIZE_ADD_SLASHES);
var_dump($urlSanitize1);
// saida
// string(15) "olá \"mundo\"!"
FILTER_SANITIZE_NUMBER_FLOAT
Remove todos os caracteres, exceto dígitos, +-
e opcionalmente .,eE
.
Este filtro aceita as flags FILTER_FLAG_ALLOW_FRACTION
(para preservar o “.”), FILTER_FLAG_ALLOW_THOUSAND
(para preservar o separador de milhar) e FILTER_FLAG_ALLOW_SCIENTIFIC
(para preservar o formato de notação cientifica).
$float1 = "a10.8b";
$float2 = "10,010.8b";
$float3 = "cc10e5";
$floatSanitize1 = filter_var($float1, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
$floatSanitize2 = filter_var($float2, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_THOUSAND | FILTER_FLAG_ALLOW_FRACTION);
$floatSanitize3 = filter_var($float3, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_SCIENTIFIC);
var_dump($floatSanitize1);
var_dump($floatSanitize2);
var_dump($floatSanitize3);
// saida
// string(4) "10.8"
// string(8) "10,010.8"
// string(4) "10e5"
FILTER_SANITIZE_NUMBER_INT
Remove todos os caracteres, exceto os dígitos, sinal de mais e menos.
$int1 = "a10b";
$int2 = "+108b";
$int3 = "cc10e5";
$intSanitize1 = filter_var($int1, FILTER_SANITIZE_NUMBER_INT);
$intSanitize2 = filter_var($int2, FILTER_SANITIZE_NUMBER_INT);
$intSanitize3 = filter_var($int3, FILTER_SANITIZE_NUMBER_INT);
var_dump($intSanitize1);
var_dump($intSanitize2);
var_dump($intSanitize3);
// saida
// string(2) "10"
// string(4) "+108"
// string(3) "105"
FILTER_SANITIZE_SPECIAL_CHARS
Codifica em HTML '"<>&
e caracteres com valor ASCII menor que 32, opcionalmente, tira ou codifica outros caracteres especiais.
Este filtro também aceita as seguintes flags:
FILTER_FLAG_STRIP_LOW
: Remove os caracteres com valor ASCII < 32FILTER_FLAG_STRIP_HIGH
: Remove os caracteres com valor ASCII > 127FILTER_FLAG_ENCODE_HIGH
: Codifica os caracteres com valor ASCII > 127FILTER_FLAG_STRIP_BACKTICK
: Remove crase dos caracteres.
<?php
$text1 = "<h1> Olá Mundo </h1>";
$textSanitize1 = filter_var($text1, FILTER_SANITIZE_SPECIAL_CHARS);
var_dump($textSanitize1);
//saida
// string(37) "<h1> Olá Mundo </h1>"
FILTER_SANITIZE_STRING
Remove tags e codifica aspas duplas e simples no estilo HTML, opcionalmente remove ou codifica caracteres HTML especiais. Codificar aspas pode ser desligado ao ativar FILTER_FLAG_NO_ENCODE_QUOTES
.
Este filtro também aceita as seguintes flags:
FILTER_FLAG_STRIP_LOW
: Remove os caracteres com valor ASCII < 32FILTER_FLAG_STRIP_HIGH
: Remove os caracteres com valor ASCII > 127FILTER_FLAG_ENCODE_LOW
: Codifica os caracteres com valor ASCII < 32FILTER_FLAG_ENCODE_HIGH
: Codifica os caracteres com valor ASCII > 127FILTER_FLAG_NO_ENCODE_QUOTES
: desativa o codificação de aspas.-
FILTER_FLAG_ENCODE_AMP
: Codifica o caractere de “&”. FILTER_FLAG_STRIP_BACKTICK
: Remove crase dos caracteres.
$text1 = '<h1>Bem vindo ao "Boteco Digital"</h1>';
$text2 = '<h1> & </h1>';
$textSanitize1 = filter_var($text1, FILTER_SANITIZE_STRING);
$textSanitize2 = filter_var($text1, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
$textSanitize3 = filter_var($text2, FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_AMP);
var_dump($textSanitize1);
var_dump($textSanitize2);
var_dump($textSanitize3);
// saida
// string(37) "Bem vindo ao "Boteco Digital""
// string(29) "Bem vindo ao "Boteco Digital""
// string(7) " & "
FILTER_SANITIZE_URL
Remove todos os caracteres, exceto letras, dígitos e $-_.+!*'(),{}|\\^~[]`<>#%";/?:@&=
.
$text1 = "https://www\t.botecodigital.dev\n.br/";
$textSanitize1 = filter_var($text1, FILTER_SANITIZE_URL);
var_dump($textSanitize1);
// saida
// string(33) "https://www.botecodigital.dev.br/"
Outras funções interessantes para serem utilizadas com filtros são a filter_var_array
e filter_input_array
, que permitem aplicar um filtro a cada um dos valores de um array ou entrada, evitando assim multiplas chamadas a função filter_var
ou filter_input
.
<?php
$data = [
' Teste ',
' "Teste" ',
" <h1>teste</h1> ",
];
$filterData = filter_var_array($data, FILTER_SANITIZE_STRING);
var_dump($filterData);
// saida
// array(3) {
// [0]=>
// string(7) " Teste "
// [1]=>
// string(17) " "Teste" "
// [2]=>
// string(7) " teste "
}
Os recursos da extensão filter do PHP são bem útiis em pequenos projetos, principalmente por não precisar de nenhuma biblioteca externa, mas para validações mais robustas pode-se utilizar alguma biblioteca própria como a Respect que é uma ótima alternativa.
Bom era isso T++.