2010-10-04

Validar y sanear datos con la extensión Filter de PHP

Introducción: la importancia de validar y sanear datos

Un punto crítico de cualquier tipo de aplicación es la entrada de datos por parte del usuario, ya que, si no se toman las medidas oportunas, un usuario puede (intencionadamente o no) causar fallos en la aplicación.
En el mundo del desarrollo web, esto cobra mucha más importancia, pues se incrementa mucho el número de usuarios potenciales y por tanto, el número de usuarios malintencionados. Ataques conocidos y fáciles de llevar a cabo son SQL Inyection y XSS (Cross Site Scripting).
En aplicaciones web es importante realizar siempre la validación en el lado del servidor.
Otra vulnerabilidad que tiene que ver con la validación, que es debido muchas veces a la inexperiencia del desarrollador, es confiar la validación de lados al lado de cliente (Javascript). Esto es algo grave, pues existen herramientas para enviar datos a una web sin que intervenga para nada un navegador ni Javascript.

La extensión Filter

Esta extensión de PHP nos expone un conjunto de funciones que nos permitirán la validación y saneamiento de datos de una forma sencilla. La funcion filter_var nos permite filtrar una variable según el filtro especificado. El orden de los parámetros es el siguiente:
  • $var: Variable que se quiere filtrar
  • $filter: Filtro que se desea aplicar. Será una constante numérica
  • $options: Conjunto de opciones que modificarán el funcionamiento del filtro. Será una constante numérica o un array
A continuación un listado de filtros de validación posibles:
FILTER_VALIDATE_BOOLEAN
Valida la variable como un booleano.
FILTER_VALIDATE_EMAIL
Valida la variable como una dirección de correo electrónico correcta.
FILTER_VALIDATE_FLOAT
Valida que la variable sea del tipo float.
FILTER_VALIDATE_INT
Valida la variable como un número entero.
FILTER_VALIDATE_IP
Valida la variable como una dirección IP.
FILTER_VALIDATE_REGEXP
Valida la variable contra una expresión regular enviada en la variable de opciones.
FILTER_VALIDATE_URL
Valida el valor coma una URL de acuerdo con la RFC 2396.
A continuación el listado de filtros para sanear:
FILTER_SANITIZE_EMAIL
Elimina todos los caracteres execpto letras, números y !#$%&’*+-/=?^_`{|}~@.[].
FILTER_SANITIZE_ENCODED
Codifica la cadena como una URL válida.
FILTER_SANITIZE_MAGIC_QUOTES
Aplica la función addslashes.
FILTER_SANITIZE_NUMBER_FLOAT
Elimina todos los caracteres excepto números, +- y opcionalmente ,.eE.
FILTER_SANITIZE_NUMBER_INT
Elimina todos los caracteres excepto números y los signos + -.
FILTER_SANITIZE_SPECIAL_CHARS
Escapa caracteres HTML y caracteres con ASCII menor a 32.
FILTER_SANITIZE_STRING
Elimina etiquetas, opcionalmente elimina o codifica caracteres especiales.
FILTER_SANITIZE_STRIPPED
Alias del filtro anterior.
FILTER_SANITIZE_URL
Elimina todos los caracteres excepto números, letras y $-_.+!*’(),{}|\\^~[]`<>#%”;/?:@&=.

Validar y sanear un número entero

Vamos a ver un sencillo ejemplo de validación de un número entero.
  1. <?php  
  2. $var = 123;  
  3. echo filter_var($var, FILTER_VALIDATE_INT);  
La función nos devolverá el entero en caso de que la validación sea correcta, en caso contrario, nos devolverá FALSE.
  1. <?php  
  2. $var = 'dos';  
  3. if(filter_var($var, FILTER_VALIDATE_INT) === false){  
  4.     echo 'Valor incorrecto';  
  5. }else{  
  6.     echo 'Valor correcto';  
  7. }  
A la hora de validar un entero, podemos jugar con las opciones para hacer ajustar el comportamiento de la función a nuestras necesidades. En el siguiente ejemplo se muestra como validar un entero que esté dentro de un rango específico:
  1. array('min_range'=>10, 'max_range' => 20));  
  2.   
  3. if(filter_var($var, FILTER_VALIDATE_INT, $options) === false){  
  4.     echo 'Valor incorrecto';  
  5. }else{  
  6.     echo 'Valor correcto';  
  7. }  
Ahora vamos a ver como sanear un entero, para los que no entiendan, sanear significa limpiar, es decir, quitamos todo lo que no tiene cabida en nuestro campo.
  1. <?php  
  2. $var = 'uno23';  
  3.   
  4. echo filter_var($var, FILTER_SANITIZE_NUMBER_INT);  
El código anterior nos devuelve ’23′ debido a que elimina todos los caracteres no numéricos de la cadena.

Validar y sanear un número float

La validación simple de un float es practicamente igual que la de un entero:
  1. <php  
  2. $var = 1.3;  
  3.   
  4. echo filter_var($var, FILTER_VALIDATE_FLOAT);  
Sin embargo, con este filtro tenemos una opcion interesante que nos permite especificar cual es el caracter que separa los decimales.
  1. <php  
  2. $var = '1,3';  
  3. $options = array('options'=>array('decimal'=>','));  
  4. echo filter_var($var, FILTER_VALIDATE_FLOAT, $options);  

Saneamiento de textos

La limpieza de los textos es algo muy importante y por ello tenemos varios flags que modifican el comporamiento del filtro FILTER_SANITIZE_STRING:
FILTER_FLAG_NO_ENCODE_QUOTES
No codificará las comillas simples ni dobles.
FILTER_FLAG_STRIP_LOW
Elimina caracteres cuyo varlor ASCII sea menor a 32.
FILTER_FLAG_STRIP_HIGH
Elimina caracteres cuyo valor ASCII sea mayor a 127.
FILTER_FLAG_ENCODE_LOW
Codifica caracteres cuyo valor ASCII sea mennor a 32.
FILTER_FLAG_ENCODE_HIGH
Codifica caracteres cuyo valor ASCII sea mayor a 127.
FILTER_FLAG_ENCODE_AMP
Codifica ampersands (&).
A continuación algunos ejemplos:
  1. <php  
  2. $text = '<p>"Hola mundo!"</p>';  
  3. echo filter_var($text, FILTER_SANITIZE_STRING);  
  4. //Resultado: &#34;Hola mundo!&#34;  
  1. <php  
  2. $text = '<p>"Hola mundo!"</p>';  
  3. echo filter_var($text, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);  
  4. //Resultado: "Hola mundo!"  
  1. <php  
  2. $text = '"Ontuts & Cokidoo"';  
  3.   
  4. echo filter_var($text, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES | FILTER_FLAG_ENCODE_AMP);  
  5. //Resultado: "Ontuts & Cokidoo"  

Obtener variables externas

Además de la función filter_var, existe una muy interesante llamada filter_input. Esta funcion nos permite obtener una variable externa por su nombre, filtrándola si es necesario.
Es importante que todas las variables externas, es decir, que nos llegan desde el lado del cliente ($_GET, $_POST, $_COOKIE…) las obtengamos a través de esta función para ahorrarnos disgustos. En el siguiente ejemplo vemos como obtener un parámetro GET de la petición con esta función:
  1. <php  
  2. $page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT);  
  3.   
  4. if($page !== null && $page !== false){  
  5.     //El parametro ha sido enviado y es un entero  
  6. }else{  
  7.     //El parametro no se ha enviado o no es un entero  
  8. }  
Esta función nos devolverá NULL si la variable no ha sido enviada, FALSE si la variable ha sido enviada pero no validada o el valor de la variable si ha sido enviada y validada.
El primer parámetro indica de que ámbito será extraída la variable, y puede ser uno de los siguientes valores:
  • INPUT_GET
  • INPUT_POST
  • INPUT_COOKIE
  • INPUT_SERVER
  • INPUT_ENV
Otra función relacionada y muy interesante es filter_input_array que nos permite definir una serie de validaciones para múltiples campos. Es decir, podemos hacer la validación todos los campos de un formulario en un solo paso. La definición de los campos con sus validaciones se hace mediante un array como se muestra en el siguiente ejemplo:
  1. <?php  
  2. $args = array(  
  3.     'product_id'   => FILTER_SANITIZE_ENCODED,  
  4.     'component'    => array('filter'    => FILTER_VALIDATE_INT,  
  5.                             'flags'     => FILTER_REQUIRE_ARRAY,  
  6.                             'options'   => array('min_range' => 1, 'max_range' => 10)  
  7.                            ),  
  8.     'versions'     => FILTER_SANITIZE_ENCODED  
  9.     );  
  10.   
  11. $myinputs = filter_input_array(INPUT_POST, $args);  
Esta función devuelve un array del tipo clave/valor que contiene los nombres de los campos junto con su filtrado.

Conclusión

Como puedes observar, PHP nos brinda una gran librería con la cual podemos llevar a cabo validaciones y saneamientos en cuestion de segundos, cosa que antes se hacía bastante engorrosa.
Ahora no hay excusa que valga para asegurarnos de que nuestros datos estén bien limpios y seguros :) .
¡Nos vemos en la próxima publicación!

No hay comentarios: