jueves, 18 de julio de 2013

Seguridad PHP - Validación y filtrado 1

La primera regla cuando tratamos con datos, ya sean provenientes de peticiones POST o GET, es que nunca nos podemos fiar de lo que pueda o no haber enviado el usuario. Por lo tanto debemos que validar cualquier dato ya que solo podemos permitir la entrada esperada. Y por supuesto no confiar únicamente en la posible validación en el lado cliente del sistema.

PHP nos presenta un conjunto de funciones que nos permitirán la validación y saneamiento de datos de una forma sencilla. La funcion filter_var (PHP >=5.2) nos permite filtrar una variable según el filtro especificado.

La sintaxis de la función es simple:

 mixed filter_var ( mixed $AFiltrar , int $tipo_filtro , mixed $opciones_filtro [opcional] );  

La salida será los datos filtrados o false en caso de fallo. Importante es el último parámetro  en el que podemos añadir opciones, en formato array asociativo o banderas. Existen diferentes formas de especificar tanto opciones como banderas:

- Para filtros que acepten opciones y banderas. Se pueden pasar en formato array asociativo multidimensional.

 $opciones = array(  
   'options' => array(  
     'default' => 3, // valor a retornar si el filtro falla  
     // más opciones aquí  
     'min_range' => 0  
   ),  
   'flags' => FILTER_FLAG_ALLOW_OCTAL,  
 );  

O podemos indicar solo opciones en un array asociativo

 $op= array('options' =>   
      array('min_range'=>10, 'max_range' => 20)  
 );  

- Para filtro que únicamente acepten banderas, se pueden introducir directamente

 $var = filter_var('oops', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);  

O podemos pasar en forma de array asociativo

 filter_var('oops', FILTER_VALIDATE_BOOLEAN,  
          array('flags' => FILTER_NULL_ON_FAILURE));  

Dependiendo de nuestra implementación, nos interesará filtrar únicamente o sanear. Vamos a ver ejemplos con los filtros más comunes tanto para filtrar como para sanear:

1. Filtrado de booleanos

El filtro FILTER_VALIDATE_BOOLEAN valida el dato pasado como booleano.

A) Posibles valores de retorno.
El valor de retorno será TRUE para "1", "true", "on" y "yes". Devuelve FALSE en caso contrario.

B) Posibles banderas:
- FILTER_NULL_ON_FAILURE: Si se especifica, devolverá FALSE sólo para "0",  "false", "off", "no", y "", y NULL para cualquier valor no booleano.

 $valor = TRUE;  
 if(filter_var($valor,FILTER_VALIDATE_BOOLEAN)) {  
      echo 'Correcto';  
 } else {  
       echo 'Error';  
 }  

2. Filtrado de enteros


El filtro FILTER_VALIDATE_INT valida si lo introducido es un entero o no.

A) Posibles opciones:
- min_range: Especifica el mínimo entero permitido.
- max_range: Especifica el máximo entero permitido.

B) Posibles banderas:
- FILTER_FLAG_ALLOW_OCTAL: Permite valores en  octal.
- FILTER_FLAG_ALLOW_HEX: Permite valores en hexadecimal.

 $valor = '122121';  
 if(filter_var($valor,FILTER_VALIDATE_INT)) {  
       echo 'Correcto';  
 } else {  
       echo 'Error';  
 }  
 $op= array('options' =>   
      array('min_range'=>10, 'max_range' => 20)  
 );  
 if(filter_var($valor, FILTER_VALIDATE_INT, $op) === false){  
      echo 'Valor incorrecto';  
 }else{  
      echo 'Valor correcto';  
 }  

3. Saneamiento de enteros

El filtro FILTER_SANITIZE_NUMBER_INT elimina todos los caracteres excepto dígitos y los signos de suma y resta.

 $valor = 'uno23';  
 echo filter_var($valor, FILTER_SANITIZE_NUMBER_INT);  

Con lo que  nos devuelve ’23′ debido a que elimina todos los caracteres no numéricos de la cadena.

4. Filtrado de flotantes

El filtro FILTER_VALIDATE_FLOAT valida si el dato pasado es un flotante o no. Por defecto el separador de decimales es el punto.

A) Posibles opciones:
- decimal: Permite especificar el carácter separador de decimales.

B) Posibles banderas:
- FILTER_FLAG_ALLOW_THOUSAND: Permite usar una coma en en separador de miles.

 $valor = '1,234';  
 if(filter_var($valor,FILTER_VALIDATE_FLOAT)) {  
       echo 'Correcto';  
 } else {  
       echo 'Error';  
 }  

En este caso el filtro retornara falso ya que $valor no es un flotante. Tendría que ser '1.234' para ser flotante.  Pero podríamos especificar que el separador para el decimal sea ','. Haremos uso de la única opción disponible.

 $valor = '1,234';  
 $op = array('options'=>array('decimal'=>','));  
 echo filter_var($valor, FILTER_VALIDATE_FLOAT, $op);  

5. Saneamiento de flotantes


El filtro FILTER_SANITIZE_NUMBER_FLOAT elimina todos los caracteres a excepción de los dígitos, +- y, opcionalmente, los caracteres  '. , e E'.

A) Posibles banderas:
- FILTER_FLAG_ALLOW_FRACTION: Permite usar un punto en el separador de decimales.
- FILTER_FLAG_ALLOW_THOUSAND: Permite usar una coma en en separador de miles.
- FILTER_FLAG_ALLOW_SCIENTIFIC: Permite notación científica con e o E.

 $numero="5-2f+3.3pp";  
 var_dump(filter_var($numero, FILTER_SANITIZE_NUMBER_FLOAT,  FILTER_FLAG_ALLOW_FRACTION));  

El resultado será:

 string(7) "5-2+3.3"  

6. Filtrado de emails

El filtro FILTER_VALIDATE_EMAIL valida si el email pasado puede existir, de acuerdo a unas reglas. No verifica su existencia.

 $valor = 'test@ejemplo.com';  
 if(filter_var($valor,FILTER_VALIDATE_EMAIL)) {  
       echo 'Correcto';  
 } else {  
       echo 'Error';  
 }  

7. Saneamiento de emails

El filtro FILTER_SANITIZE_EMAIL elimina todos los caracteres menos letras, dígitos y !#$%&'*+-/=?^_`{|}~@.[].

 $var="some(one)@exa\\mple.com";  
 var_dump(filter_var($var, FILTER_SANITIZE_EMAIL));  

 string(19) "someone@example.com"  

8. Filtrado de una URL

El filtro  FILTER_VALIDATE_URL valida si el valor recibido pertenece a una URL valida. No verifica su existencia.

A) Posibles banderas:
- FILTER_FLAG_SCHEME_REQUIRED: Requiere que URL cumpla esquema RFC (http://ejemplo). Por defecto.
- FILTER_FLAG_HOST_REQUIRED: Requiere que la URL tenga el nombre del host (http://www.ejemplo.com). Por defecto.
- FILTER_FLAG_PATH_REQUIRED: Requiere que la URL tenga una ruta tras el nombre del dominio (www.ejemplo.com/test/).
- FILTER_FLAG_QUERY_REQUIRED: Requiere que la URL tenga una 'query string' ("test.php?name=Jose&age=40").

 $valor = 'http://www.ejemplo.com';  
 if(filter_var($valor,FILTER_VALIDATE_URL)) {  
       echo 'Correcto';  
 } else {  
       echo 'Error';  
 }  
 $url = "example.php?name=Jose&age=40";  
 if(!filter_var($url, FILTER_VALIDATE_URL,FILTER_FLAG_QUERY_REQUIRED)){  
      echo "URL no válida";  
 }  
 else  
 {  
      echo "URL válida";  
 }  

Cuidado con el uso de este filtro ya que puede haber problemas al validar como correcto posible código XSS.

9. Saneamiento de una URL

El filtro FILTER_SANITIZE_URL elimina todos los caracteres excepto letras, dígitos y $-_.+!*'(),{}|\\^~[]`<>#%";/?:@&=.

 $var="http://www.ejemploåøs.coøm";  
 echo filter_var($var, FILTER_SANITIZE_URL);  
 //Resultado: http://www.ejemplo.com  

10. Filtrado de una IP


El filtro FILTER_VALIDATE_IP se encarga de validar si la ip introducida es correcta. No comprueba su existencia.

A) Posibles banderas:
- FILTER_FLAG_IPV4: Requiere que la ip sea un valor IPv4 válido (ej: 192.168.0.12)
- FILTER_FLAG_IPV6: Requiere que la ip sea un valor IPv6 IP válido (ej: 2001:0db8:85a3:08d3:1319:8a2e:0370:7334)
- FILTER_FLAG_NO_PRIV_RANGE: Requiere que la ip no este en los siguientes rango privados IPv4: 10.0.0.0/8, 172.16.0.0/12 y 192.168.0.0/16. Además si la ip es IPv6 no puede empezar por FD o FC.
- FILTER_FLAG_NO_RES_RANGE: Requiere que la ip no este en alguno de los siguientes rangos IPv4 reservados: 0.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24 y224.0.0.0/4. Este flag no se aplica a direcciones IPv6.

 $valor = '192.168.0.1';  
 if(filter_var($value01,FILTER_VALIDATE_IP)) {  
       echo 'Correcto';  
 } else {  
       echo 'Error';  
 }  
 $ip = "2001:0db8:85a3:08d3:1319:8a2e:0370:7334";  
 if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) echo "Error";  
 else echo "correcto";  

11. Filtrado de una expresión regular

El filtro FILTER_VALIDATE_REGEXP valida un valor con una expresión regular compatible con Perl.

A) Opciones (obligatoria):
- regexp: Especifica la expresión regular contra la que validaremos el valor pasado.

 $email = "test@ejemplo.com";  
 $patron = "/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU";  
 if(filter_var($email, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>$patron))) === false) echo "Fallo";     
 else echo "Correcto";  

12. Saneamiento de textos

El filtro FILTER_SANITIZE_STRING Elimina etiquetas y  opcionalmente elimina o codifica caracteres especiales. Por defecto, cualquier carácter no valido en una cadena. Como por ejemplo, los tags de html.

A) Posibles banderas:
- FILTER_FLAG_NO_ENCODE_QUOTES: Si se indica esta opción, las comillas simples (') y las dobles (") no se codificarán.
- FILTER_FLAG_STRIP_LOW: Elimina todos los caracteres con valor ASCII < 32.
- FILTER_FLAG_STRIP_HIGH: Elimina todos los caracteres con valor ASCII >127.
- FILTER_FLAG_ENCODE_LOW: Codifica todos los caracteres con valor ASCII < 32.
- FILTER_FLAG_ENCODE_HIGH: Codifica todos los caracteres con valor ASCII >127.
- FILTER_FLAG_ENCODE_AMP: Codifica ampersands (&) a &

 $value = "<script>alert('Cuidado!');</script>";  
 echo filter_var($value, FILTER_SANITIZE_STRING);  
 //Resultado: alert('Cuidado!');  

Usando el filtro FILTER_SANITIZE_ENCODED conseguimos el mismo resultado pero codificado como si hubiésemos aplicado urlencode(). Si embargo hay que tener en cuenta que no tiene disponible las banderas FILTER_FLAG_NO_ENCODE_QUOTES ni  FILTER_FLAG_ENCODE_AMP. Las cuatro restantes si que están disponibles.

13. Saneamiento de caracteres especiales

El filtro FILTER_SANITIZE_SPECIAL_CHARS se encarga de convertir caracteres especiales en entidades HTML. Equivalente a llamar a la función htmlspecialchars().

A) Posibles banderas:
- FILTER_FLAG_STRIP_LOW: Elimina caracteres con código ASCII < 32.
- FILTER_FLAG_STRIP_HIGH: Elimina caracteres con código ASCII > 127.
- FILTER_FLAG_ENCODE_HIGH: Codifica caracteres con código ASCII < 32.

 $value = "<script>alert('Cuidado!');</script>";  
 echo filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);  
 //Resultado(ver código fuente de la página): &#60;script&#62;alert(&#39;Cuidado!&#39;);&#60;/script&#62;)  


Entradas relacionadas

Seguridad PHP - Validación y filtrado 2

No hay comentarios:

Publicar un comentario