Bobby tables - A importância da validação e higienização das entradas
Bobby tables – A importância da validação e higienização das entradas

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 &#34;vai&#34; 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 < 32
  • FILTER_FLAG_STRIP_HIGH: Remove os caracteres com valor ASCII > 127
  • FILTER_FLAG_ENCODE_LOW: Codifica os caracteres com valor ASCII < 32
  • FILTER_FLAG_ENCODE_HIGH: Codifica os caracteres com valor ASCII > 127
  • FILTER_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 < 32
  • FILTER_FLAG_STRIP_HIGH: Remove os caracteres com valor ASCII > 127
  • FILTER_FLAG_ENCODE_HIGH: Codifica os caracteres com valor ASCII > 127
  • FILTER_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) "&#60;h1&#62; Olá Mundo &#60;/h1&#62;"

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 < 32
  • FILTER_FLAG_STRIP_HIGH: Remove os caracteres com valor ASCII > 127
  • FILTER_FLAG_ENCODE_LOW: Codifica os caracteres com valor ASCII < 32
  • FILTER_FLAG_ENCODE_HIGH: Codifica os caracteres com valor ASCII > 127
  • FILTER_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 &#34;Boteco Digital&#34;"
// string(29) "Bem vindo ao "Boteco Digital""
// string(7) " &#38; "

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) " &#34;Teste&#34; "
//   [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++.