Python: Sistema de sesiones (Google App Engine)

Primero toca explicar qué es un sistema de sesiones. Cuando se programa para web, se debe tener en cuenta que, puesto que el cliente y el servidor, son entidades bien diferenciadas, que sólo se ponen en contacto cuando el cliente (en este caso el navegador del usuario) hace una petición.

Esta cosa tan simple, base del protocolo TCP/IP, sobre el cual que se erige el HTTP, es obviado por mucha gente que programa usando las herramientas inadecuadas, pero esa es otra historia1.

La pregunta surge entonces ¿cómo identificamos eficazmente al usuario que navega por nuestras páginas? Puede que por el navegador, el sistema operativo, la IP, o todo a la vez. Pero no es suficiente, porque toda esa información no es de fiar pues puede falsearse, y en el caso de la IP, suele pasar que hayan miles de personas con la misma a la vez.

¿No hay un identificador único para el usuario? La respuesta es rotundamente no, por motivos de privacidad, a menos que uses Google Chrome (no es un bug, es una feature).

Pero no estéis tristes, todavía queda por probar algo: las cookies2. Sí, esas pequeñas ilegibles y endemoniadas cadenas de texto que web sí, y otra también, van llenando poco a poco, lenta pero incansablemente, tu disco duro, hasta explotar en un maremágnum de bytes a cascoporro.

Por todo ello, tan sólo deberíamos mantener una cookie con un id único de sesión, para saber quien nos está pidiendo cada cosa. Dicho id debería ser aleatorio y volátil, y debería ser el servidor, y no la cookie, quien decide cuando caduca para evitar suplantaciones de sesión. La información relativa a la sesión, al usuario, y demás se guardaría en el servidor, lejos de todo peligro.

Sin embargo, puede ser que queramos guardar algún tipo de información en las cookies del cliente, ya sea por comodidad o para validar la sesión con información extra. Dicha información, obviamente no debería ser "muy peligrosa", porque aunque la cifremos (y guardemos la clave del cifrado en el servidor junto con la id de sesión) existe la amenaza potencial de que alguien rompa el cifrado. Se podría decir que aplicamos el cifrado solo para evitar los script kiddies.

Para guardar y obtener datos de esta segunda cookie lo oṕtimo es usar un serializador seguro (recordemos que las cookies pueden ser manipuladas), como el presentado días atrás, y cifrándolo con una clave generada aleatoriamente y se guardaría en el servidor.

Teniendo todo esto en cuenta, he desarrollado una librería para Google App Engine, aunque podría ser adaptada fácilmente para otros usos simplemente cambiando la forma en la que las cabeceras son enviadas al cliente, y cómo son guardados los valores en el servidor (usa memcache). Podéis descargarla (46.2 KiB) desde las descargas del blog en Google Code.

Usarlo, con Google App Engine, resulta francamente fácil.

from google.appengine.ext.webapp.util import run_wsgi_app

from google.appengine.ext import webapp
from appSession import Session

class RequestWeb(webapp.RequestHandler):
def get(self):
session = Session(self)
if session["visited"]:
text = "Times visited %d." % session["visited"]
session["visited"] += 1
else:
text = "First time on page."
session["visited"] = 1

session["_foo"] = "This string will be stored on server."
session["bar"] = "This string will be stored on a cookie."

session.write()
self.response.out.write(
"<html><head></head><body><p>%s</p></body></html>" % text
)

application = webapp.WSGIApplication([
('/',RequestWeb)
],
debug=True)

run_wsgi_app(application)

EDITO 2011.08.30: Ejemplo corregido para evitar la llamada del /favicon.ico

En este ejemplo, el número de veces que se ha visitado la página, en lo que dura la sesión, es guardado en una cookie, junto con otra cadena de texto. Y otra cadena de texto es guardada en el servidor.

Espero que os sea útil.

  1. Si empiezo con el tema, no pararé hasta que se me agoten las pilas.
  2. No es realmente la última opción: la especificación del HTML5 añade al navegador la posibilidad de mantener una especie de base de datos en el navegador, pero su aceptación está tardando eones.

5 comentarios:

  1. gracias por compartir tu trabajo!!!

    @coto

    ResponderEliminar
  2. Es curioso, pero el session["visited"] += 1 nunca me funciona.

    ResponderEliminar
  3. Por favor, añádelo a la lista de bugs en http://code.google.com/p/s26blog/issues/list
    Muchas versiones del SDK de Appengine han salido desde que programé esa librería, lo corregiré en cuando pueda.

    ResponderEliminar
  4. Bueno, en versiones recientes de appengine se elimina la interfaz que estaba usando para escribir en las cabeceras, ya está corregido, incluyendo el PRSerializer nuevo y la documentación corregida conforme al formato de pydoc. Gracias por reportar.

    ResponderEliminar
  5. Hola como instalo la libreria?
    gracias

    ResponderEliminar