El paquete django.contrib.csrf
provee protección contra Cross-site request
forgery (CSRF) (falsificación de peticiones inter-sitio).
CSRF, también conocido como "session riding" (montado de sesiones) es un exploit de seguridad en sitios Web. Se presenta cuando un sitio Web malicioso induce a un usuario a cargar sin saberlo una URL desde un sitio al cual dicho usuario ya se ha autenticado, por lo tanto saca ventaja de su estado autenticado. Inicialmente esto puede ser un poco difícil de entender así que en esta sección recorreremos un par de ejemplos.
14.5.1. Un ejemplo simple de CSRF
Supongamos que posees una cuenta de webmail en example.com
. Este sitio
proveedor de webmail tiene un botón Log Out que apunta a la URL
example.com/logout
— esto es, la única acción que necesitas realizar para
desconectarte (log out) es visitar la página example.com/logout
.
Un sitio malicioso puede coercerte a visitar la URL example.com/logout
incluyendo esa URL como un <iframe>
oculto en su propia página maliciosa. De
manera que si estás conectado (logged in) a tu cuenta de webmail del sitio
example.com
y visitas la página maliciosa, el hecho de visitar la misma te
desconectará de example.com
.
Claramente, ser desconectado de un sitio de webmail contra tu voluntad no es un incidente de seguridad aterrorizante, pero este tipo de exploit puede sucederle a cualquier sitio que "confía" en sus usuarios, tales como un sitio de un banco o un sitio de comercio electrónico.
14.5.2. Un ejemplo más complejo de CSRF
En el ejemplo anterior, el sitio example.com
tenía parte de la culpa debido
a que permitía que se pudiera solicitar un cambio de estado (la desconexión del
sitio) mediante el método HTTP GET
. Es una práctica mucho mejor el requerir
el uso de un POST
HTTP para cada petición que cambie el estado en el
servidor. Pero aun los sitios Web que requieren el uso de POST
para acciones
que signifiquen cambios de estado son vulnerables a CSRF.
Supongamos que example.com
ha mejorado su funcionalidad de desconexión de
manera que "Log Out" es ahora un botón de un <form>
que es enviado vía un
POST
a la URL example.com/logout
. Adicionalmente, el <form>
de
desconexión incluye un campo oculto:
<input type="hidden" name="confirm" value="true" />
Esto asegura que un simple POST
a la URL example.com/logout
no
desconectará a un usuario; para que los usuarios puedan desconectarse, deberán
enviar una petición a example.com/logout
usando POST
y enviar la
variable POST
confirm
con el valor 'true'
.
Bueno, aun con dichas medidas extra de seguridad, este esquema también puede
ser atacado mediante CSRF — la página maliciosa sólo necesita hacer un poquito
más de trabajo. Los atacantes pueden crear un formulario completo que envíe su
petición a tu sitio, ocultar el mismo en un <iframe>
invisible y luego usar
JavaScript para enviar dicho formulario en forma automática.
14.5.3. Previniendo la CSRF
Entonces, ¿Cómo puede tu sitio defenderse de este exploit?. El primer paso es
asegurarse que todas las peticiones GET
no posean efectos colaterales. De
esa forma, si un sitio malicioso incluye una de tus páginas como un
<iframe>
, esto no tendrá un efecto negativo.
Esto nos deja con las peticiones POST
. El segundo paso es dotar a cada
<form>
que se enviará vía POST un campo oculto cuyo valor sea secreto y sea
generado en base al identificador de sesión del usuario. Entonces luego, cuando
se esté realizando el procesamiento del formulario en el servidor, comprobar
dicho campo secreto y generar un error si dicha comprobación no es exitosa.
Esto es precisamente lo que hace la capa de prevención de CSRF de Django, tal como se explica en la siguiente sección.
14.5.3.1. Usar el middleware CSRF
El paquete django.contrib.csrf
contiene sólo un módulo: middleware.py
.
Este módulo contiene una clase middleware Django: CsrfMiddleware
la cual
implementa la protección contra CSRF.
Para activar esta proteccion, agrega
'django.contrib.csrf.middleware.CsrfMiddleware'
a la variable de
configuración MIDDLEWARE_CLASSES
en tu archivo de configuración. Este
middleware necesita procesar la respuesta después de SessionMiddleware
,
así que CsrfMiddleware
debe aparecer antes que SessionMiddleware
en la lista (esto es debido que el middleware de respuesta es procesado de
atrás hacia adelante). Por otra parte, debe procesar la respuesta antes que la
misma sea comprimida o alterada de alguna otra forma, de manera que
CsrfMiddleware
debe aparecer después de GZipMiddleware
. Una vez que has
agregado eso a tu MIDDLEWARE_CLASSES
ya estás listo. Revisa la sección
"Orden de MIDDLEWARE_CLASSES
_" en el Capítulo 13
_ si necesitas conocer más
sobre el tema.
En el caso en el que estés interesado, así es como trabaja CsrfMiddleware
.
Realiza estas dos cosas:
- Modifica las respuestas salientes a peticiones agregando un campo de
formulario oculto a todos los formularios
POST
, con el nombrecsrfmiddlewaretoken
y un valor que es un hash del identificador de sesión más una clave secreta. El middleware no modifica la respuesta si no existe un identificador de sesión, de manera que el costo en rendimiento es despreciable para peticiones que no usan sesiones. - Para todas las peticiones
POST
que porten la cookie de sesión, comprueba quecsrfmiddlewaretoken
esté presente y tenga un valor correcto. Si no cumple estas condiciones, el usuario recibirá un errorHTTP
403. El contenido de la página de error es el mensaje "Cross Site Request Forgery detected. Request aborted."
Esto asegura que solamente se puedan usar formularios que se hayan originado en tu sitio Web para enviar datos vía POST al mismo.
Este middleware deliberadamente trabaja solamente sobre peticiones HTTP POST
(y sus correspondientes formularios POST). Como ya hemos explicado, las
peticiones GET
nunca deberían tener efectos colaterales; es tu
responsabilidad asegurar eso.
Las peticiones POST
que no estén acompañadas de una cookie de sesión no son
protegidas simplemente porque no tiene sentido protegerlas, un sitio Web
malicioso podría de todas formas generar ese tipo de peticiones.
Para evitar alterar peticiones no HTML, el middleware revisa la cabecera
Content-Type
de la respuesta antes de modificarla. Sólo modifica las páginas
que son servidas como text/html
o application/xml+xhtml
.
14.5.3.2. Limitaciones del middleware CSRF
CsrfMiddleware
necesita el framework de sesiones de Django para poder
funcionar. (Revisa el Capítulo 12 para obtener más información sobre sesiones).
Si estás usando un framework de sesiones o autenticación personalizado que
maneja en forma manual las cookies de sesión, este middleware no te será de
ayuda.
Si tu aplicación crea páginas HTML y formularios con algún método inusual (por
ej. si envía fragmentos de HTML en sentencias JavaScript document.write
),
podrías estár salteandote el filtro que agrega el campo oculto al formulario.
De presentarse esta situación, el envío del formulario fallará siempre. (Esto
sucede porque CsrfMiddleware
usa una expresión regular para agregar el campo
csrfmiddlewaretoken
a tu HTML antes de que la página sea enviada al cliente,
y la expresión regular a veces no puede manejar código HTML muy extravagante).
Si sospechas que esto podría estar sucediendo, sólo examina el código en tu
navegador Web para ver si es que csrfmiddlewaretoken
ha sido insertado en tu
<form>
.
Para más información y ejemplos sobre CSRF, visita http://en.wikipedia.org/wiki/CSRF.