Protección contra ataques de Cross Site Request Forgery

El middleware y la etiqueta de plantilla CSRF proporciona una protección fácil de usar contra ataques de Cross Site Request Forgeries. Este tipo de ataque ocurre cuando un sitio web malicioso contiene un enlace, un botón de formulario o algún JavaScript que está destinado a realizar alguna acción en tu sitio web, utilizando las credenciales de un usuario conectado que visita el sitio malicioso en su navegador. Un tipo relacionado de ataque, “login CSRF”, donde un sitio atacante engaña al navegador de un usuario para que se conecte a un sitio con las credenciales de alguien más, también está cubierto.

La primera defensa contra ataques CSRF es asegurarse de que las solicitudes GET (y otros métodos “seguros”, como se define en RFC 9110 Section 9.2.1) sean sin efectos laterales. Las solicitudes a través de métodos “inseguros”, como POST, PUT y DELETE, pueden entonces ser protegidas siguiendo los pasos descritos en Cómo utilizar la protección contra ataques CSRF de Django.

Cómo funciona

La protección CSRF se basa en las siguientes cosas:

  1. Una cookie CSRF que es un valor secreto aleatorio, a la que otros sitios no tendrán acceso.

    CsrfViewMiddleware envía esta cookie con la respuesta siempre que django.middleware.csrf.get_token() sea llamado. También puede enviarla en otros casos. Por razones de seguridad, el valor del secreto se cambia cada vez que un usuario se conecta.

  2. Un campo oculto de formulario con el nombre “csrfmiddlewaretoken”, presente en todos los formularios POST salientes.

    Los textos traducidos manteniendo todas sus etiquetas intactas son:

    Esta parte se realiza mediante la etiqueta de plantilla csrf_token.

  3. Para todas las solicitudes entrantes que no utilicen HTTP GET, HEAD, OPTIONS o TRACE, debe estar presente una cookie CSRF y el campo “csrfmiddlewaretoken” debe estar presente y correcto. Si no lo está, el usuario recibirá un error 403.

    Al validar el valor del campo “csrfmiddlewaretoken”, solo se compara la clave secreta con la clave secreta en el valor de la cookie. Esto permite el uso de tokens que cambian constantemente. Aunque cada solicitud puede utilizar su propio token, la clave secreta sigue siendo común a todas.

    Esta comprobación se realiza mediante CsrfViewMiddleware.

  4. CsrfViewMiddleware verifica el encabezado Origin header, si está proporcionado por el navegador, contra el host actual y la configuración CSRF_TRUSTED_ORIGINS. Esto proporciona protección contra ataques de subdominios cruzados.

  5. Además, para solicitudes HTTPS, si no se proporciona el encabezado Origin, CsrfViewMiddleware realiza una comprobación estricta del referente. Esto significa que incluso si un subdominio puede establecer o modificar cookies en su dominio, no podrá forzar a un usuario a enviar una solicitud POST a su aplicación ya que esa solicitud no provendrá de su propio dominio exacto.

    Esto también aborda un ataque «man-in-the-middle» posible bajo HTTPS cuando se utiliza una clave secreta independiente de la sesión, debido al hecho de que los encabezados HTTP Set-Cookie (desafortunadamente) son aceptados por los clientes incluso cuando están hablando con un sitio bajo HTTPS. (La comprobación del referente no se realiza para solicitudes HTTP porque la presencia del encabezado Referer no es lo suficientemente fiable bajo HTTP.)

    Si la configuración CSRF_COOKIE_DOMAIN está establecida, el referente se compara con ella. Puede permitir solicitudes de subdominios cruzados incluyendo un punto líder. Por ejemplo, CSRF_COOKIE_DOMAIN = '.example.com' permitirá solicitudes POST desde www.example.com y api.example.com. Si la configuración no está establecida, entonces el referente debe coincidir con el encabezado HTTP Host.

    Expandir los referentes aceptados más allá del host actual o dominio de cookie se puede hacer mediante la configuración CSRF_TRUSTED_ORIGINS.

Este es el resultado de la traducción:

Ignora intencionalmente las solicitudes GET (y otras solicitudes definidas como “seguras” por RFC 9110 Section 9.2.1). Estas solicitudes nunca deberían tener efectos laterales potencialmente peligrosos, y por lo tanto un ataque CSRF con una solicitud GET debería ser inocuo. RFC 9110 Section 9.2.1 define POST, PUT y DELETE como “inseguros”, y todos los otros métodos se asumen inseguros también, para una protección máxima.

La protección CSRF no puede proteger contra ataques en el medio, por lo que utilice HTTPS con Seguridad de Transporte Estricto. También asume la validación del encabezado HOST y que no hay vulnerabilidades de inyección de código en sitios web en su sitio (ya que las vulnerabilidades XSS ya permiten a un atacante hacer cualquier cosa que una vulnerabilidad CSRF permite y mucho peor).

Eliminar el encabezado Referer

Para evitar revelar la URL del remitente a sitios de terceros, es posible que desee desactivar el remitente en las etiquetas <a> de su sitio. Por ejemplo, podría utilizar la etiqueta <meta name="referrer" content="no-referrer"> o incluir el encabezado Referrer-Policy: no-referrer. Debido a que la protección CSRF realiza un control estricto del remitente en solicitudes HTTPS, esas técnicas causan una falla CSRF en solicitudes con métodos “inseguros”. En su lugar, utilice alternativas como <a rel="noreferrer" ...>" para enlaces a sitios de terceros.

Limitaciones

Los subdominios dentro de un sitio podrán establecer cookies en el cliente para todo el dominio. Al establecer la cookie y utilizar un token correspondiente, los subdominios podrán evadir la protección CSRF. La única forma de evitar esto es asegurarse de que los subdominios estén controlados por usuarios confiables (o, al menos, no puedan establecer cookies). Tenga en cuenta que incluso sin CSRF, hay otras vulnerabilidades, como la fijación de sesión, que hacen que dar subdominios a partes no confiables sea una mala idea, y estas vulnerabilidades no pueden fácilmente ser corregidas con los navegadores actuales.

Herramientas

Los ejemplos a continuación asumen que está utilizando vistas basadas en funciones. Si está trabajando con vistas basadas en clases, puede referirse a Decorar vistas basadas en clases.

csrf_exempt(view)[fuente]

Este decorador marca una vista como exenta de la protección asegurada por el middleware. Ejemplo:

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt


@csrf_exempt
def my_view(request):
    return HttpResponse("Hello world")
csrf_protect(view)

Decorator que proporciona la protección de CsrfViewMiddleware a una vista.

Uso:

from django.shortcuts import render
from django.views.decorators.csrf import csrf_protect


@csrf_protect
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)
requires_csrf_token(view)

Normalmente, el etiqueta de plantilla csrf_token no funcionará si CsrfViewMiddleware.process_view o una equivalente como csrf_protect no se ha ejecutado. Se puede utilizar la decoradora de vista requires_csrf_token para asegurarse de que la etiqueta de plantilla funcione. Esta decoradora funciona de manera similar a csrf_protect, pero nunca rechaza una solicitud entrante.

Ejemplo:

from django.shortcuts import render
from django.views.decorators.csrf import requires_csrf_token


@requires_csrf_token
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)

Este decorador fuerza a una vista a enviar la cookie CSRF.

Configuración

Un número de configuraciones pueden ser utilizadas para controlar el comportamiento CSRF de Django:

Preguntas Frecuentes

¿Es una vulnerabilidad enviar un par de tokens CSRF arbitrarios (cookie y datos POST)?

No, esto es por diseño. Sin un ataque man-in-the-middle, no hay forma para que un atacante envíe un token CSRF a la cookie de un navegador de un víctima, por lo tanto un ataque exitoso necesitaría obtener la cookie del navegador de la víctima mediante XSS o similar, en cuyo caso un atacante usualmente no necesita ataques CSRF.

Algunas herramientas de auditoría de seguridad marcan esto como un problema pero, como se mencionó antes, un atacante no puede robar la cookie CSRF del navegador de un usuario. «Robar» o modificar tu propio token utilizando Firebug, herramientas de desarrollo de Chrome, etc., no es una vulnerabilidad.

¿Es un problema que la protección CSRF de Django no esté vinculada a una sesión por defecto?

No, esto es por diseño. No vincular la protección CSRF a una sesión permite utilizar la protección en sitios como un pastebin que permiten las submission de usuarios anónimos que no tienen una sesión.

Si deseas almacenar el token CSRF en la sesión del usuario, utiliza la configuración CSRF_USE_SESSIONS.

¿Por qué podría un usuario experimentar una falla de validación CSRF después de iniciar sesión?

Por razones de seguridad, los tokens CSRF se rotan cada vez que un usuario inicia sesión. Cualquier página con un formulario generado antes de una sesión iniciada tendrá un token CSRF antiguo e inválido y necesitará ser recargada. Esto podría suceder si un usuario utiliza el botón atras después de iniciar sesión o si inician sesión en una pestaña diferente del navegador.