Menu
17 abril, 2011 Capitán SEO

Seguridad Web. Formularios Web y URL’s (Parte 1)

Seguridad Web. Formularios Web y URL’s (Parte 1)
5 (100%) 1 vote

 

En este artículo vamos a exponer el procesamiento de los formularios web (<form>) y los ataques más frecuentes que hay que tener en cuenta a la hora de trabajar con datos enviados a través de los formularios y de las URL’s.

Vamos a centrarnos en los siguientes puntos:

1. Formularios Web y datos
2. Ataque Semántico de la URL
3. Ataques de File Upload
4. Cross-Site Scripting (XSS)
5. Cross-Site Request Forgeries (CSRF)
6. Spoofed Form Submition
7. Spoofed Http Request

1. Formularios web y datos

Los datos pueden llegar de dos fuentes: de un usuario o de una entidad remota. Podemos considerar dos tipos distintos de datos:

  • Datos filtrados
  • Datos corruptos

Todos los datos creados por nosotros son fiables y podemos considerarlos como datos filtrados. Un ejemplo de dato que podemos crear es cualquier dato hardcoded, como una dirección de correo:

$email = 'user@ejemplo.com';

Esta dirección de correo: user@ejemplo.com, no proviene de ninguna fuente remota. Es por esto que se considera de confianza. Cualquier dato que proviene de una fuente remota es input y todo input debe ser considerado corrupto y filtrarlo antes de utilizarlo en nuestra aplicación. El input puede ser: los datos que provienen de los formularios web, el correo que recibimos a través del servidor IMAP o un documento XML enviado por otra aplicación. En el ejemplo anterior $email es una variable que contiene un dato filtrado. El dato es la parte más importante de la relación y no la variable porque esta es sólo un contenedor para datos y puede ser reemplazado por datos corruptos:

$email = $_POST['email'];

PHP ofrece unas cuantas variables predefinidas a las que se puede acceder desde cualquier parte del script que se está ejecutando. Estas variables nos dan bastante información específica sobre el entorno de la aplicación. Una de estas variables predefinidas es $_POST que contiene toda la información pertinente a los parámetros enviados a través del método POST. Para utilizar las variables predefinidas, el parámetro de configuración track_vars tiene que estar activado en php.ini (con PHP 4.03 track_vars está activado por defecto).

Si no queremos que los datos cambien se puede utilizar una constante en lugar de una variable:

define('EMAIL', 'user@ejemplo.com');

En este caso, EMAIL está definida como constante cuyo valor es ‘user@ejemplo.com’ para lo que dure el script. EMAIL guarda su valor aunque por error le asignemos otro.

Ejemplo: el siguiente código muestra user@ejemplo.com. El intento de redefinir EMAIL genera una notificación:

<?php

define('EMAIL', 'user@ejemplo.com');

define('EMAIL', 'another_user@ejemplo.com');

echo EMAIL;

?>

Aún el usuario puede enviar datos por varios métodos, la mayoría de las aplicaciones toma las más importantes acciones como resultado de envíos de formularios. Como un atacador puede hacer daño a nuestra aplicación manipulando datos con los que esta hace un proceso, los formularios son uno de los problemas más importantes en la seguridad de las aplicaciones web porque dan información sobre los datos que la aplicación está esperando.

Un usuario puede enviar datos a nuestra aplicación por 3 métodos predominantes:

  • Por URL (datos GET)
  • En el contenido de una solicitud (datos POST)
  • En la cabecera  HTTP (datos COOKIE)

Los datos de los formularios se envían a a través del método de solicitud GET or POST. Cuando creamos un formulario HTML especificamos el método de solicitud en el atributo method de la etiqueta <form>:

<form action="http://www.ejemplo.com/registrar.php" method="GET">

Cuando se utiliza el método GET, el navegador envía los datos del formulario como parte de la URL. Por ejemplo:

<form action="http://www.ejemplo.com/login.php" method="GET">

<p>Usuario: <input type="text" name="nombre_usuario" /></p>

<p>Clave: <input type="password" name="clave_usuario" /></p>

<p><input type="submit" /></p>

</form>

Si ponemos el usuario jorge y la clave tierra, llegamos a http:// www.ejemplo.com/login.php? nombre_usuario= jorge& clave_usuario= tierra después de enviar los datos del formulario. La solicitud más sencilla  HTTP/1.1 válida para esta URL es:

GET / login.php? nombre_usuario= jorge& clave_usuario= tierra HTTP/1.1

HOST: www.ejemplo.com

No es necesario utilizar el formulario HTML para solicitar esta URL. De hecho, no hay ninguna diferencia entre una solicitud GET enviada como resultado de un formulario y una enviada como resultado de un clic del usuario sobre un enlace.

Hay que tener en cuenta que si ponemos en el atributo action de la etiqueta <form> una cadena de consulta será reemplazada por los datos del formulario si se utiliza como método de solicitud el método GET.

Si el atributo method contiene un valor inválido o si  method  no se especifica, el navegador utilizará por defecto el método de solicitud GET.

Para ilustrar el método de solicitud POST, consideramos el ejemplo anterior con una simple modificación del atributo method de la etiqueta <form>. Vamos a poner POST en lugar de GET.

<form action="http://www.ejemplo.com/login.php" method="POST">

<p>Usuario: <input type="text" name="nombre_usuario" /></p>

<p>Clave: <input type="password" name="clave_usuario" /></p>

<p><input type="submit" /></p>

</form>

Si ponemos de nuevo el usuario jorge y la clave tierra, llegamos a http:// www.ejemplo.com/login.php después de enviar los datos del formulario. Estos están en el contenido de la solicitud HTTP y no como cadena de consulta de la URL solicitada. La más solicitud más sencilla HTTP/1.1 válida para este ejemplo es:

GET / login.php  HTTP/1.1

HOST: www.ejemplo.com

Content-Type: applicación/x-www-form-urlencoded

Content-Length: 41

nombre_usuario=jorge&clave_usuario= tierra

Hasta ahora hemos visto los principales métodos que utiliza un usuario para enviar datos a nuestra aplicación. El siguiente punto habla sobre como los atacadores se pueden aprovechar de nuestros formularios y URL’s utilizándolos como puertas abiertas hacía nuestra aplicación.

2. Ataque Semántico de la URL

Este tipo de ataque implica que el usuario modifique la URL con el fin de descubrir si se puede hacer algo. Por ejemplo, si el usuario jorge pincha sobre un enlace de nuestra aplicación web y llega a: http://www.ejemplo.com/privado.php?usuario=jorge, se puede considerar que este usuario intentará ver qué pasa cuando el valor usuario cambie. Por ejemplo, podrá visitar la URL http://www.ejemplo.com/privado.php?usuario=luis para ver si puede tener acceso a la información de otra persona. Por esto, los datos GET son un target más frecuente para atacadores sobre todo nuevos.

Para ilustar mejor un ataque de manipulación de URL y cómo una vulnerabilidad puede pasar desapercibida vamos a considerar el ejemplo de una aplicación web de email donde los usuarios pueden iniciar sesión y comprobar sus cuentas de correo. Cualquier aplicación que pide a sus usuarios que inicien sesión tiene que ofrecer un mecanismo para recuperar la contraseña. Una técnica habitual es de poner una pregunta al usuario cuya respuesta es poco probable que un atacador conozca y de enviar un correo con la nueva contraseña a la dirección de correo ya registrada en la cuenta del usuario.

El fin de esto es enviarle la clave a esta dirección y también registrar la dirección de email alternativa para futuro uso. El formulario siguiente pregunta al usuario por una dirección de email alternativa. El nombre de la cuenta cuya clave será cambiada está identificado en un campo hidden del formulario.

<form action="reset.php" method="GET">

<input type="hidden" name="usuarior" value="jorge" />

<p>Por favor, escriba la dirección de correo donde quieres recibir la nueva clave:</p>

<input type="text" name="email" /><br />

<input type="submit" value="Enviar clave" />

</form>

El script que recibe los datos del formulario, reset.php, tiene toda la información que necesita para enviar el email con la nueva clave.

Si un usuario llega e este formulario (después de contestar correctamente a la pregunta de seguridad) podemos considerar que el usuario no es un impostor sino el usuario legítimo de la cuenta jorge. Si este usario proporciona jorge@otroejemplo.com como dirección alternativa de correo, entonces llega a esta URL después de pinchar el botón submit del formulario:

http://www.ejemplo.com/reset.php?usuario=jorge@email=jorge%40otroejemplo.com

Esta URL es la que aparece en la barra del navegador. Un usuario que pasa por todo este proceso puede fácilmente identificar el fin de las variables usuario y email. Después de todo esto, el usuario decide que elmejor@ejemplo.com es una dirección de correo que le gusta y puede probar suerte accediendo directamente a la siguiente URL:

http://www.ejemplo.com/reset.php?usuario=elmejor@email=jorge%40otroejemplo.com

Si reset.php “confía” en los datos proporcionados por el usuario entonces es vulnerable a un ataque semántico de la URL. Una nueva clave será generada por la cuenta elmejor y será enviada a jorge@otroejemplo.com, permitiéndole acceder a la cuenta elmejor.

Si utilizamos sesiones podemos fácilmente evitar esta clase de situaciones:

<?php

session_start();

$email_ok = array();


$email_pattern = '/^[^@\s<&>]+@([-a-z0-9]+\.) + [a-z]{2,}$/i';

 

if (preg_match($email_pattern, $_POST['email'])) {

$email_ok['email'] = $_POST['email'];

$usuario = $_SESSION['usuario'];

$nueva_clave = md5(uniqid(rand(), TRUE));

 

If ($_SESSION['verificado']) {

/* Actualizar Clave */

mail($email_ok['email'] , 'La nueva clave', $nueva_clave);

}

}

?>

Aunque este ejemplo omite algunos detalles muestra una dirección de email sospechosa proporcionada por el usuario, las variables de sesión que registran si el usuario ha contestado correctamente a la pregunta de seguridad ($_SESSION[‘verificado’]) y el nombre de la cuenta asociada a la pregunta de seguridad ($_SESSION[‘usuario’]).

$_SESSION es otra variable predefinida de PHP, igual que $_GET, $_POST. Contiene la información de todas las variables de sesión. Registrar información de sesión ofrece la posibilidad de utilizar esta información  a través de toda la aplicación web sin necesidad de pasar los datos con GET or POST.

'/^[^@\s<&>]+@([-a-z0-9]+\.) + [a-z]{2,}$/i'.

Se utiliza la sintaxis de las expresiones regulares de estilo PEARL y no POSIX. La cadena está delimitada por /.

^  significa el comienzo del texto.  [^@\s<&>]  que el texto no contenga @, \s – un espacio en blanco,

<, &, >. +  que la expresión anterior tiene que coincidir por lo menos una vez o sea que haya un texto antes de @ que cumpla la condición [^@\s<&>]. ([-a-z0-9]+\.)  cualquier carácter en el intervalo a-z y cualquier dígito de 0-9.   \.  es el carácter “.” El punto sólo “.” en una expresión regular que significa cualquier carácter. Para que sea interpretado como punto normal hay que ponerle antes un backslash. {2,} significa que la expresión anterior tiene que coincidir por lo menos 2 veces o sea que el texto después del carácter “.” contenga por lo menos 2 caracteres. $ define el final del texto. i significa case-insensitive.

La falta de confianza en los datos input es la llave para prevenir dejar las puertas abiertas a los atacadores.

Fuente: Chris Shiflett, PHP Security

Autor: Alexandru Costache

Tweet about this on TwitterShare on Facebook0Share on Google+0Share on LinkedIn0Pin on Pinterest0

About the Author

Comments (2)

    • capitanseo

      Hola Sanosuke, la parte dos llegará. ¿Cuándo? No tenemos respuesta. Es difícil mantener un blog actualizado cuando se tiene trabajo pero le damos vida cada vez que el tiempo nos lo permite. Gracias por tu comentario y saludos.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *