El libro de Django 1.0

12.1. Cookies

Los desarrolladores de navegadores hace tiempo que se dieron cuenta de que esta carencia de estados iba a representar un problema para los desarrolladores web, y así fue como nacieron las cookies (literalmente galleta). Una cookie es una pequeña cantidad de información que el servidor delega en el navegador, de forma que este la almacena. Cada vez que el cliente web solicita una página del servidor, se le envía de vuelta la cookie.

Veamos con un poco más de detalle el funcionamiento. Cuando abrimos nuestro navegador y escribimos google.com, el navegador envía una solicitud HTTP a Google que empieza más o menos así:

GET / HTTP/1.1
Host: google.com
...

Cuando Google responde, la respuesta contiene algo parecido a esto:

HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671;
            expires=Sun, 17-Jan-2038 19:14:07 GMT;
            path=/; domain=.google.com
Server: GWS/2.1
...

Fíjate en la línea que comienza con Set-Cookie. El navegador almacenará el valor indicado (PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671) y se lo volverá a enviar a Google cada vez que vuelva a acceder a alguna de sus páginas; de esa forma, la próxima vez que vuelvas a Google, la petición que enviará el navegador se parecerá a esta:

GET / HTTP/1.1
Host: google.com
Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671
...

Google puede saber ahora, gracias al valor de la Cookie, que eres la misma persona que accedió un rato antes. Este valor puede ser, por ejemplo, una clave en una tabla de la base de datos que almacene los datos del usuario. Con esa información, Google puede hacer aparecer tu nombre en la página (de hecho, lo hace).

12.1.1. Cómo definir y leer los valores de las cookies

A la hora de utilizar las capacidades de persistencia de Django, lo más probable es que uses las prestaciones de alto nivel para la gestión de sesiones y de usuarios, prestaciones que discutiremos un poco más adelante en este mismo capítulo. No obstante, ahora vamos a hacer una breve parada y veremos como leer y definir cookies a bajo nivel. Esto debería ayudarte a entender como funcionan el resto de las herramientas que veremos en el capítulo, y te será de utilidad si alguna vez tienes que trabajar con las cookies directamente.

Obtener los valores de las cookies que ya están definidas es muy fácil. Cada objeto de tipo petición, request, contiene un objeto COOKIES que se comporta como un diccionario; puedes usarlo para leer cualquier cookie que el navegador haya enviado a la vista:

def show_color(request):
    if "favorite_color" in request.COOKIES:
        return HttpResponse("Your favorite color is %s" % \
            request.COOKIES["favorite_color"])
    else:
        return HttpResponse("You don't have a favorite color.")

Definir los valores de las cookies es sólo un poco más complicado. Debes usar el método set_cookie() en un objeto de tipo HttpResponse. He aquí un ejemplo que define la cookie favorite_color utilizando el valor que se le pasa como parámetro GET:

def set_color(request):
    if "favorite_color" in request.GET:

        # Create an HttpResponse object...
        response = HttpResponse("Your favorite color is now %s" % \
            request.GET["favorite_color"])

        # ... and set a cookie on the response
        response.set_cookie("favorite_color",
                            request.GET["favorite_color"])

        return response

    else:
        return HttpResponse("You didn't give a favorite color.")

Hay una serie de parámetros opcionales que puedes pasar a response.set_cookie() y que te permiten controlar determinadas características de la cookie,:

  • max_age: el tiempo (en segundos) que la cookie debe permanecer activa. Si este parámetro es la cookie, desaparecerá automáticamente cuando se cierre el navegador. (valor por defecto: None)
  • expires: la fecha y hora en que la cookie debe expirar. Debe estar en el formato "Wdy, DD-Mth-YY HH:MM:SS GMT". Si se utiliza este parámetro, su valor tiene preferencia sobre el definido mediante max_age. (valor por defecto: None)
  • path: la ruta o path para la cual es válida la cookie. Los navegadores solo reenviarán la cookie a las páginas que estén en dicha ruta. Esto impide que se envíe esta cookie a otras secciones de la web. Es especialmente útil si no se tiene el control del nivel superior de directorios del servidor web. (valor por defecto: "/")
  • domain: el dominio para el cual es válida la cookie. Se puede usar este parámetro para definir una cookie que sea apta para varios dominios. Por ejemplo, definiendo domain=".example.com" la cookie será enviada a los dominios www.example.com, www2.example.com y aun.otro.subdominio.example.com. Si a este parámetro no se le asigna ningún valor, la cookie solo será enviada al dominio que la definió. (valor por defecto: "/")
  • secure: si este valor se define como True, se le indica al navegador que sólo retorne esta cookie a las páginas que se accedan de forma segura (protocolo HTTPS en vez de HTTP). (valor por defecto: False)

12.1.2. Las cookies tienen doble filo

Puede que te hayas dado cuenta de algunos de los problemas potenciales que se presentan con esto de las cookies; vamos a ver algunos de los más importantes:

El almacenamiento de los cookies es opcional; los navegadores no dan ninguna garantía. De hecho, los navegadores permiten al usuario definir una política de aceptación o rechazo de las mismas. Para darte cuenta de lo muy usadas que son las cookies en la web actual, simplemente activa la opción de "Avisar antes de aceptar cualquier cookie" y date un paseo por Internet.

A pesar de su uso habitual, las cookies son el ejemplo perfecto de algo que no es confiable. Esto significa que el desarrollador debe comprobar que el usuario está dispuesto a aceptar las cookies antes de confiar en ellas.

Aún más importante, nunca debes almacenar información fundamental en las cookies. La Web rebosa de historias de terror acerca de desarrolladores que guardaron información irrecuperable en las cookies del usuario, solo para encontrarse con que el navegador había borrado todos esos datos por cualequier razón.

Las Cookies no son seguras (especialmente aquellas que no se envían mediante HTTPS) Dado que los datos enviados viajan en texto claro, están expuestas a que terceras personas lean esa información, lo que se llama ataques de tipo snooping (por snoop, fisgonear, husmear). Por lo tanto, un atacante que tenga acceso al medio puede interceptar la cookie y leer su valor. El resultado de esto es que nunca se debe almacenar información confidencial en una cookie.

Hay otro tipo de ataque, aún más insidioso, conocido como ataque man-in-the-middle o MitM (ataque de tipo Hombre-en-medio o Intermediario). Aquí, el atacante no solo intercepta la cookie, sino que además la usa para actuar ante el servidor como si fuera el usuario legítimo. El Capítulo 19 describe en profundidad este tipo de ataques, así como formas de prevenirlo.

Las Cookies ni siquiera son seguras para los servidores. La mayoría de los navegadores permiten manipular y editar de forma sencilla los contenidos de cookies individuales, y existen herramientas como mechanize (http://wwwsearch.sourceforge.net/mechanize/) que permiten a cualquiera que esté lo suficientemente motivado construir solicitudes HTTP a mano.

Así que tampoco debemos almacenar en las cookies datos que sean fáciles de falsificar. El error habitual en este escenario consiste en almacenar algo así como IsLoggedIn=1 en una cookie cuando el usuario se ha validado. Te sorprendería saber cuantos sitios web cometen este tipo de error; no lleva más de unos segundos engañar a sus sistemas de "seguridad".