Esta traducción está incompleta. Por favor, ayuda a traducir este artículo del inglés.
En la mayoría de los casos, el propósito de un Formulario HTML es mandar datos a un servidor. El servidor procesa los datos y envía una respuesta al usuario. Esto parece simple, pero es importante tener algunas cosas en mente para asegurar que los datos no dañaran al servidor o causaran problemas a sus usuarios.
A donde van los datos?
Arquitectura cliente/servidor
La web está basada en una arquitectura cliente/servidor muy básica que puede resumirse de la siguiente manera: un cliente (usualmente un navegador web) envía una petición (Request) al servidor (en la mayoría de los casos un servidor web como Apache, Nginx, IIS, Tomcat, etc.), utilizando el protocolo HTTP. El servidor responde (Response) a la petición mediante el mismo protocolo.
Del lado del cliente, un formulario HTML es solo una manera conveniente y sencilla de configurar una petición HTTP para enviar datos al servidor. Permite al usuario proporcionar la información que será enviada en la petición HTTP.
Del lado del cliente: definir como enviar los datos
El elemento <form>
define como se enviarán los datos. Todos sus atributos están diseñados para permitirle configurar la petición que será enviada cuando el usuario haga clic en el botón de envío. los dos atributos más importantes son: action
y method
.
Atributo action
Este atributo define a donde se enviarán los datos. Su valor debe ser una URL valida. Si este atributo no es proporcionado, los datos se enviaran a la URL de la página que contiene el formulario HTML.
Ejemplos:
En éste ejemplo, los datos son enviados a la URL https://foo.com:
<form action="https://foo.com">
En éste otro, los datos son enviados al mismo servidor que aloja la página del formulario, pero a una URL diferente del mismo servidor:
<form action="/somewhere_else">
Cuando no se especifican atributos, el atributo <form>
causa que los datos sean enviados a la URL de la página que incluye el formulario:
<form>
Muchas páginas antiguas, utilizan la siguiente notación para indicar que los datos deben ser enviados a la página que contiene el formulario; ésto fue necesario hasta que en el HTML5 se requirió el atributo action
. Ahora, ésto ya no es necesario.
<form action="#">
Nota: Puede especificar una URL que utilice el protocolo HTTPS (HTTP seguro). Cuando se hace ésto, los datos son encriptados junto con el resto del requerimiento, aún si el formulario está alojado en una página insegura que es accedida utilizando HTTP. Por otro lado, si la forma esta alojada en una página segura, pero se especifica una URL HTTP insegura en el atributo action
, todos los navegadores web lanzaran una alerta de seguridad al usuario cada vez que intente enviar datos, pues los datos no estarían encriptados.
Atributo method
Este atributo define como se envían los datos. El Protocolo HTTP proporciona varias maneras de realizar una petición; los datos de los formularios HTML pueden ser enviados en al menos 2 de ellas: el método GET y el método POST.
Para entender la diferencia entre estos dos métodos, detengamonos un momento y examinemos como funciona el HTTP. Cada vez que desea obtener un recurso de la web, el navegador envía una petición a una URL. Una petición HTTP se compone de dos partes: un encabezado que contiene un conjunto de meta datos globales sobre las características del navegador, y un cuerpo que puede contener la información necesaria para que el servidor procese la petición específica.
Método GET
El método GET
es el método usado por el navegador web para pedir al servidor el envío de un recurso determinado: "Hey servidor, quiero obtener este recurso." En este caso, el navegador web envía un cuerpo vacío. Como el cuerpo está vacío, si un formulario es enviado utilizando este método, los datos enviados al servidor se agregan a la URL.
Ejemplo
Considere el siguiente formulario:
<form action="https://foo.com" method="get"> <input name="say" value="Hi"> <input name="to" value="Mom"> <button>Send my greetings</button> </form>
Con el método GET
, la petición HTTP se vería como:
GET /?say=Hi&to=Mom HTTP/1.1 Host: foo.com
Método POST
El método POST
es un poco diferente. Es el método en el que el navegador web envía una petición al servidor para solicitar una respuesta que tome en cuenta los datos proporcionados en el cuerpo de la petición HTTP: "Hey servidor, toma en cuenta estos datos y envíame el resultado apropiado." Si se envía un formulario usando este método, los datos son agregados al cuerpo de la petición HTTP.
Ejemplo
Considere esta forma (igual a la anterior):
<form action="https://foo.com" method="post"> <input name="say" value="Hi"> <input name="to" value="Mom"> <button>Send my greetings</button> </form>
Al enviarla utilizando el método POST, la petición HTTP se vería como ésta:
POST / HTTP/1.1 Host: foo.com Content-Type: application/x-www-form-urlencoded Content-Length: 13 say=Hi&to=Mom
El encabezado Content-Length
indica el tamaño del cuerpo, y el encabezado Content-Type
indica el tipo de recurso enviado al servidor. Revisaremos los encabezados mas adelante.
Por supuesto, una petición HTTP nunca es mostrada al usuario (Si desea verla, necesita una herramienta como Web Console de Firefox o las herramientas de desarrollo de Chrome Chrome Developer Tools). Lo único que se muestra al usuario es la URL llamada. Así que, con una petición GET
, el usuario puede ver los datos en la barra de URL, pero con una petición POST
, no puede ver los datos en la barra de URL. Ésto es importante por dos razones:
- Si necesita enviar un password (o alguna pieza sensible de datos), nunca utilice el método
GET
pues corre el riesgo de mostrarlo en la barra de URL. - Si necesita enviar una gran cantidad de datos, es preferible usar el método
POST
pues algunos navegadores limitan el tamaño de la URL. De la igual manera, muchos servidores limitan el tamaño de la URL que reciben.
Del lado del servidor: recuperando los datos
Independientemente del método HTTP elegido, el servidor recibe una cadena que debe decodificar para obtener los datos como una lista de pares de valores: llave/valor. La forma de acceder a esta lista, depende específicamente de la plataforma de desarrollo y el framework que esté utilizando. La tecnología utilizada también determina como manejar las llaves duplicadas; sin embargo, se da mayor prioridad al valor más recientemente obtenido para una llave dada.
Ejemplo: PHP
PHP ofrece algunos objetos globales para acceder a los datos. Asumiendo que se ha utilizado el método POST
, el siguiente ejemplo obtiene los datos y los muestra al usuario. Por supuesto, que hacer con los datos le corresponde a usted. Podría mostrarlos, almacenarlos en una base de datos, enviarlos por correo electrónico o procesarlos de cualquier otra manera.
<?php // la variable global $_POST le permite acceder a los datos enviados con el método POST // Para acceder a los datos enviados con el método GET, puede usar $_GET $say = htmlspecialchars($_POST['say']); $to = htmlspecialchars($_POST['to']); echo $say, ' ', $to;
Éste ejemplo, muestra una página con los datos que enviamos. La salida de nuestro ejemplo sería:
Hi Mom
Ejemplo: Python
Éste ejemplo usa Python para mostrar los mismos datos en una página web. Se usa el paquete CGI Python package para acceder a los datos del formulario.
#!/usr/bin/env python import html import cgi import cgitb; cgitb.enable() # para manejo de errores print("Content-Type: text/html") # encabezado HTTP indicando que sigue codigo HTML print() # linea en blanco, fin de encabezados form = cgi.FieldStorage() say = html.escape(form["say"].value); to = html.escape(form["to"].value); print(say, " ", to)
El resultado es el mismo que con PHP:
Hi Mom
Otros lenguajes y frameworks
Existen otras tecnologías del lado del servidor para manejar formularios , incluyendo Perl, Java, .Net, Ruby, etc. Solo seleccione su preferida. Dicho esto, debemos señalar que es muy raro usar estas tecnologías directamente porque puede ser demasiado complicado. Es más común y sencillo utilizar un framework que facilite el manejo de formularios, tales como:
- Symfony para PHP
- Django para Python
- Ruby On Rails para Ruby
- Grails para Java
- etc.
Cabe aclarar, que aun con el uso de un framework, trabajar con formularios no es necesariamente fácil. Pero es mucho mejor y le ahorrará tiempo.
Un caso especial: enviando archivos
Los archivos son un caso especial al trabajar con formularios HTML. Son datos binarios —o al menos, se consideran así— cualquier otro dato es considerado texto. Como HTTP es un protocolo de texto, existen requerimientos especiales para manejar datos binarios.
Atributo enctype
Este atributo, nos permite especificar el encabezado HTTP Content-Type
. Este encabezado es muy importante, pues le dice al servidor que tipo de datos se están enviando. Por default, su valor es: application/x-www-form-urlencoded
. En términos coloquiales, esto significa: "Los datos de éste formulario han sido codificados en la URL del formulario."
Pero si desea enviar archivos, necesita hacer dos cosas:
- Establecer el atributo
method
comoPOST,
puesto que el contenido del archivo no puede colocarse en un parámetro de la URL que utiliza el formulario. - Establecer el valor del atributo
enctype
comomultipart/form-data
puesto que los datos serán divididos en múltiples partes, una para cada archivo y otra para el cuerpo del formulario que sera enviado junto con ellos.
Por ejemplo:
<form method="post" enctype="multipart/form-data"> <input type="file" name="myFile"> <button>Send the file</button> </form>
Nota: Algunos navegadores dan soporte al atributo multiple
en el elemento <input>
para enviar más de un archivo en un solo elemento de entrada. Como maneja el servidor estos archivos depende realmente de la tecnología utilizada en el servidor. Reiterando, utilizar un framework hará su vida más fácil.
Precaución: Muchos servidores están configurados con limites de tamaño para los archivos y solicitudes HTTP y así prevenir abusos. Es importante checar estos limites con el administrador del servidor antes de enviar un archivo.
Consideraciones de seguridad
Cada vez que envía datos a un servidor, necesita tener en mente la seguridad. Los formularios HTML son una de las primeras líneas de ataque contra los servidores. El problema no viene del formulario HTML mismo; sino de la forma en que el servidor maneja los datos.
Fallas comunes de seguridad
Dependiendo de lo que se esté haciendo, existen algunos riesgos de seguridad bien conocidos:
XSS y CSRF
Cross-Site Scripting (XSS) y Cross-Site Request Forgery (CSRF) son tipos de ataque comunes que ocurren cuando se muestran los datos enviados por el usuario de regreso al mismo usuario o a otros usuarios.
Los ataques XSS permiten a los atacantes inyectar un script del lado del cliente en páginas web vistas por otros usuarios. La vulnerabilidad al script cross-site puede ser utilizada por los atacantes para brincar los controles de acceso tales como same origin policy (política de mismo origen). El efecto de estos ataques puede ir desde una pequeña molestia a un significante riesgo de seguridad.
Los ataques CSRF son similares a los ataques XSS ya que inician de la misma forma—inyectando scripts del lado del cliente a las páginas web —pero su objetivo es diferente. Los atacantes CSRF intentan escalar los privilegios del usuario hacia los de otro usuario de mayor nivel (tal como el administrador del sitio) para ejecutar acciones que podrían no estar disponibles en el nivel actual (por ejemplo, enviar datos a usuarios no autentificados).
Los ataques XSS explotan la confianza que el usuario tiene por un sitio web, mientras que los ataques CSRF explotan la confianza que un sitio web tiene para sus usuarios.
Para prevenir estos ataques, siempre debe checar los datos que el usuario envía al servidor y (si necesita mostrarlos) no debe mostrar el contenido HTML tal y como es proporcionado por el usuario. En vez de eso, debe procesar los datos proporcionados para no mostrarlos textualmente. Casi todos los frameworks en el mercado implementan o soportan una mínima cantidad de filtros que eliminan elementos de código HTML de los datos enviados por el usuario básicamente <script>
, <iframe>
y <object>
. Ésto ayuda a mitigar el riesgo, pero no lo elimina.
Inyección de SQL
La inyección de SQL es un tipo de ataque que intenta ejecutar acciones en la base de datos del sitio web objetivo. Típicamente consiste en enviar una solicitud SQL con la esperanza de que el servidor la ejecute (la mayoría de las veces cuando el servidor de aplicaciones intenta almacenar los datos). Este es actualmente una de las principales líneas de ataque contra los sitios web.
Las consecuencias pueden ser terribles y van desde perdida de datos hasta acceso a la infraestructura completa utilizando escalamiento de privilegios. Esta es una amenaza muy seria y nunca debe almacenar datos enviados por un usuario sin haberlos sometido a un proceso de desinfección (por ejemplo, debe utilizar mysql_real_escape_string()
en una infraestructura PHP/MySQL).
Inyección de encabezados HTTP y correos electrónicos
Éstos tipos de ataque ocurren cuando su aplicación construye encabezados HTTP o correos electrónicos basados en los datos obtenidos de un usuario en un formulario. No dañan directamente al servidor o afectan a los usuarios, pero son puertas abiertas para problemas más profundos como: hackeo de sesiones o ataques de phishing.
La mayoría de estos ataques son silenciosos y pueden convertir el servidor en un zombie.
Sea paranoico: Nunca confíe en los usuarios
Entonces, ¿como luchar contra estas amenazas? Éste tema está fuera del alcance de ésta guía, pero existen algunas reglas que necesita tener en mente. La más importante es: Nunca, pero nunca, confíe en los usuarios, incluyendose a sí mismo; aún un usuario autentificado podría haber sido hackeado.
Siempre y sin excepción, todos los datos que llegan a su servidor deben ser checados y desinfectados.
- Se deben generar secuencias de escape para caracteres potencialmente peligrosos. Los caracteres específicos con que debe tener cuidado varían dependiendo del contexto en que los datos son usados y la plataforma del servidor empleado. Todos los lenguajes del lado del servidor tienen funciones para ello.
- Limite la cantidad de datos que entran, de manera que permita únicamente lo necesario.
- Genere un almacén para los archivos cargados (almacenelos en un servidor diferente y permita el acceso al archivo únicamente atravez de un sub dominio diferente o aun mejor mediante un nombre de dominio completamente diferente).
Puede evitar muchos problemas si sigue estas tres reglas, pero siempre es mejor tener un esquema de seguridad revisado por un tercero competente. No asuma nunca que ha visto todos los problemas de seguridad posibles.
Conclusión
Como puede ver, enviar datos en formularios es fácil, pero hacer segura una aplicación puede resultar muy difícil. Recuerde que el desarrollador de la presentación de la aplicación, no es el único que debe definir los modelos de seguridad de los datos. Sí, como también vimos, es posible mejorar la validación de los datos del lado del cliente pero el servidor no puede confiar en éstas validaciones, pues no tiene manera de saber con certeza lo que pasó del lado del cliente.
Ver también
Si desea saber más sobre la seguridad en una aplicación web, puede consultar los siguientes recursos: