martes, 2 de julio de 2013

Formularios y PHP

Cuando un usuario rellena un formulario HTML y presiona el botón (input) de tipo 'submit' toda la información que este haya introducido en los distintos controles será enviado a la dirección (script) y mediante el método indicado.
Ciclo de envío de formulario a un servidor PHP

El método utilizado para el envío de los datos al servidor, indicará como viajan los datos hacia él.

A) GET

- Todos los nombres de los campos y valores viajan por la URL (query strings).
- Como son mostrados por la URL el navegador los almacena en su historial. Lo que no es muy conveniente para el caso de información sensible y más si ese ordenador puede ser usado por más personas. Además que el usuario despistado puede compartir datos al compartir la URL.
- Debido a esta hay una restricción de longitud de los datos. La longitud máxima de la URL es de 2048 caracteres.
- Los datos enviados al servidor, al viajar por la URL, pueden quedar almacenados en los ficheros de 'logs' del mismo.
- Solo se permite enviar datos ASCII.
- Resultados de operaciones GET pueden ser cacheadas. Lo que quiere decir que múltiples llamadas a un servicio pueden resultar una única petición.
- Tipo de codificación (enctype): application/x-www-form-urlencoded. Es el tipo por defecto, además del único, por lo que no es necesario indicarlo.

B) POST

- Todos los nombres de los campos y valores viajan en el cuerpo de la petición HTTP.
- No hay limite de longitud de los datos.
- Datos no expuestos en URL o 'logs' de servidor.
- Permite en envió de información de tipo binario. Lo que lo hace apto para el envió de ficheros.
- No se puede cachear operaciones POST.
- Tipo de codificación: application/x-www-form-urlencoded, por defecto, y multipart/form-data para envió de ficheros.

En cuanto a seguridad, ninguno de ellos aporta una mejora real de seguridad respecto al otro. Si bien es cierto que el POST no expone información a través de la URL o 'logs' del servidor, la información igual viaja en texto plano. Aunque evidentemente es mejor utilizar POST para el envío de datos sensibles y dejar GET para simples peticiones de información. Para una verdadera seguridad necesitaríamos usar certificados y HTTPS.

Vamos a partir del siguiente formulario html:

 <div id="contacto">  
   <fieldset>  
     <legend>Formulario de contacto</legend>  
     <form action="contacto.php" method="post">  
       <p>  
         <label for="nombre">Nombre</label>  
         <input type="text" name="nombre" id="nombre" />  
       </p>  
       <p>  
         <label for="telefono">Num. Teléfono</label>  
         <input type="text" name="telefono" id="telefono" />  
       </p>  
       <p>  
         <label for="email">Email</label>  
         <input type="text" name="email" id="email" />  
       </p>        
       <p>  
         <label for="mensaje">Mensaje</label>  
         <textarea name="mensaje" id="mensaje"></textarea>  
       </p>        
       <p>  
         <input type="submit" id="enviar" value="Enviar"/>  
       </p>  
     </form>  
   </fieldset>  
 </div>  

Cuando el formulario es enviado, los nombres de los campos del formulario y sus valores viajan en el cuerpo de la petición POST hacia el servidor. Más concretamente hacia contacto.php. Dicha petición podría ser parecida a esta:

 POST /contacto.php HTTP/1.1  
 Host: localhost  
 User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.63 Safari/537.31  
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8  
 Accept-Language: en-us,en;q=0.5  
 Accept-Encoding: gzip,deflate  
 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7  
 Keep-Alive: 300  
 Connection: keep-alive  
 Referer: localhost/prueba.php  
 Content-Type: application/x-www-form-urlencoded  
 Content-Length: 43  
 nombre=Jose&telefono=123456789&email=ejemplo@prueba.com&mensaje=hola...&enviar=Enviar  

Todas las variables (nombre,telefono...) serán accesibles desde el script PHP en el servidor. Ya que serán almacenadas en el array superglobal asociativo $_POST. Si se hubiera usado GET como método de envío, accederíamos a las variables mediante el array superglobal $_GET.
Por lo tanto para acceder al valor de dichas variables accederíamos así $nombre = $_POST['nombre'], $email = $_POST['email']...
Desde PHP 5.3 la directiva de configuración de PHP 'register_globals' fue declarada obsoleta y por lo tanto desactivada por defecto. Ya que permitía la creación de variables remotamente. Y aunque tenía la ventaja de crear  automáticamente variables para cada uno las claves de dichos arrays asociativos de $_POST o $_GET. Esto supone un fallo de seguridad. No se podía que permitir que los parámetros que se enviaban a través de la URL se convirtiesen automáticamente en variables al llegar al servidor.

Validación

Vamos a ver como podemos validar los datos provenientes del formulario. No vamos a tener en cuenta la inyección de sql u otros problemas de seguridad. Nos centraremos en una simple validación mediante expresiones regulares.

Para ello utilizaremos la siguiente función de PHP:

 preg_match_all(string $patronBusqueda, string $cadena )  

Mediante esta función trataremos de buscar la concordancia del patrón especificado (primer parámetro)  en la cadena (segundo parámetro).

Ejemplos de utilización:

- Validar nombre. Puesto que se es posible insertar nombre y apellidos vamos a permitir solo caracteres y espacios en blanco. También añadiremos para el ejemplo una comprobación de máximo de caracteres. Y un  mínimo de 3 caracteres.

 function validarCadena($cadena,$max){  
   $expr = '/^[a-zA-Z ]{3,}$/';  
   if(preg_match($expr, $cadena) && strlen($cadena)<=(int)$max) return true;  
   else return false;  
 }  

- Validar teléfono. Vamos a suponer que se puede insertar tanto móvil como fijo (formato de España).

 function validarTelefono($telefono){   
   $expr = '/^[9|8|6|7][0-9]{8}$/';  
   return preg_match($expr, $telefono);  
 }  

- Validar email.

 function validarEmail($email) {  
   $expr = '/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/';  
   return preg_match($expr, $email);  
 }  

Ejemplo de utilización

 function validarCadena($cadena, $max) {  
   $expr = '/^[a-zA-Z ]{3,}$/';  
   if (preg_match($expr, $cadena) && strlen($cadena) <= (int) $max)  
     return true;  
   else  
     return false;  
 }  
 function validarEmail($email) {  
   $expr = '/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/';  
   return preg_match($expr, $email);  
 }  
 function validarTelefono($telefono) {  
   $expr = '/^[9|8|6|7][0-9]{8}$/';  
   return preg_match($expr, $telefono);  
 }  
 $errores = array();  
 $cadenaErrores = "";  
 if (isset($_POST['nombre'], $_POST['telefono'], $_POST['email'], $_POST['mensaje'])) {  
   $nombre = $_POST['nombre'];  
   $telefono = $_POST['telefono'];  
   $email = $_POST['email'];  
   $mensaje = $_POST['mensaje'];  
   $max = 60;  
   if (!validarCadena(trim($nombre), $max))  
     $errores[] = "El nombre solo admite letras y espacios en blanco. Debe de contener entre 3 y $max caracteres.";  
   if (!validarTelefono($telefono))  
     $errores[] = "El número de teléfono introducido no es válido";  
   if (!validarEmail($email))  
     $errores[] = "El email introducido no es válido";  
   if (empty($errores)) {  
     //hacer algo con los datos  
   } else {  
     //cadena con todos los errores que podemos devolver al cliente  
     foreach ($errores as $error) {  
       $cadenaErrores .= "<p>$error</p>";  
     }  
   }  
 }  

La función trim(string $cadena) elimina los espacios en blanco al principio y al final de la cadena. Ya que estos espacios en blanco no nos interesan para la validación  Solo nos pueden llegar a interesar los espacios en blanco entre caracteres (para el nombre).

Otras validaciones útiles

- Validar fecha con formato DD/MM/AAAA

 function validarFecha($fecha){  
   $expr = '/^\d{1,2}\/\d{1,2}\/\d{4}$/';  
   return preg_match($expr, $fecha);
 }  

- Validar contraseña. Supondremos unos requisitos: mínimo de 8 caracteres y al menos  de un carácter en minúscula, uno en minúscula y un dígito

 function validarConstraseña($pass){  
   $expr = '/^.*(?=.{8,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).*$/';  
   return preg_match($expr, $pass);  
 }  

Mirar el uso de declaraciones de búsqueda hacia adelante en expresiones regulares.

- Funciones destinadas a validar un tipo concreto de valor. Destinadas por ejemplo a saber si se ha introducido una edad valida o una cantidad:
  • is_string(): Función que retornará true si la variable evaluada es una cadena. 
  • is_int(): Función que retornará true si la variable evaluada es un entero. 
  • is_float():Función que retornará true si la variable evaluada es un flotante. 
  • is_numeric: Función que retornará true si la variable evaluada es un número o un string numérico. 
  • is_boolean(): Función que retornará true si la variable evaluada es de tipo bool.

No hay comentarios:

Publicar un comentario