[pyar] [Django] Como cachear cuando hay CSRF tokens?

Andres Riancho andres.riancho en gmail.com
Jue Jul 17 09:25:29 ART 2014


Gracias a todos por responder! Les cuento lo que hice al final, para
que algun otro que este perdido con esto pueda usar mi experiencia de
estos dias.

    Lo que proponen en la lista es agregar este codigo JS:

// JS code
$.ajax({
    url: // your csrf url,
    type: 'GET',
    data: {type: 'login'},  // only if you need a session id for cookie login
    dataType: 'json',
    success: function(data) {
        $('form').each(function() {
            $(this).append(
                '<input type=hidden name=csrfmiddlewaretoken ' +
                    ' value="' + data.token + '">');
        });
    }
});

    No renderear el {{ csrftoken }} en en template, y tomarlo de una
view que se ve similar a:

// Django code
# views.py, don't forget to add to urls.py
def get_csrf(request):
    if request.GET.get('type') == 'login':
        request.session.set_test_cookie()
    return JSONResponse({
        'status': 1,
        'token': getattr(request, 'csrf_token', 'NOTPROVIDED')
    })

    De esta manera, cualquier usuario que accede al site ve el mismo
HTML (que en el mejor de los casos sale del cache) y el CSRF token se
agrega una vez que el HTML se cargó. Se puede mejorar ese JS haciendo
que se cargue el token solo cuando el form esté visible/activo/siendo
completado por el usuario para no tener +1 request HTTP que luego no
se use.

    El problema que puede traer esto es la manera en la cual se
verifica el token CSRF. La verificacion de CSRF en Django, por
default, envia un input hidden con un valor random y envia el mismo
valor en una session cookie. Esta cookie es definitivamente mala para
los caches:

"Varnish will, in the default configuration, not cache a object coming
from the backend with a Set-Cookie header present. Also, if the client
sends a Cookie header, Varnish will bypass the cache and go directly
to the backend." [0]

    Veamos que pasaría con la solucion propuesta de CSRF token por JS:

1- HTTP GET /
2- Respuesta 200 OK sin cookies con el HTML del cache

3- HTTP GET /csrf-token
4- Respuesta 200 OK con Set-Cookie para verificar luego CSRF
        Set-Cookie: csrftoken=3hUXXXR9niBQVBwQsCqtsbzNZugFx1pH;
expires=Thu, 16-Jul-2015 12:12:01 GMT; Max-Age=31449600; Path=/;
secure

5- HTTP POST /form (usa cookie csrf)
6- Respuesta 200 OK del backend (no cache). Esto esta perfecto, nunca
vamos a esperar que Varnish (u otro cache) responda a un POST. En este
caso que tenga la cookie de csrf no cambia nada.

# El usuario sigue navegando por el site despues de haber completado el form

7- HTTP GET /about-us (usa cookie csrf)
    * La cookie se envia porque como vemos arriba:
        - El path es / (todo el domain)
        - La expiración es larga (horas)
8- Respuesta 200 OK proveniente del backend! <------ MALO
    * Notar que cualquier otro usuario que hubiese navegado
directamente a /about-us, sin previamente haber obtenido el token CSRF
de la pagina principal donde esta el form, hubiese realizado el
request SIN cookies, y entonces obtenido la respuesta del cache.

Uf! Que problema... entonces? Entonces hay que configurar el cache de
alguna manera especial o tomar la (potencialmente) arriesgada decision
de deshabilitar CSRF para los forms.

Luego de analizar mi situacion especifica, no siendo esto lo mejor
para todos los casos! Deshabilite CSRF tokens para los formularios que
estan pre-login. De esta manera el problema inicial: "Como cacheo HTML
que tiene tokens csrf?" y tambien el caso que enumeraba antes
desaparecen.

Otro detalle que me encontré es que el tracker de Google Analytics
setea una cookie, la cual es enviada tambien al site. Si no se sacan
estas cookies [1], el cache va a ir hasta el backend para obtener la
respuesta cada vez, haciendo que el cache sea simplemente un
pasa-manos.

Espero les sirva!

[0] https://www.varnish-cache.org/docs/3.0/tutorial/cookies.html
[1] https://www.varnish-cache.org/trac/wiki/VCLExampleRemovingSomeCookies

2014-07-16 21:24 GMT-03:00 Pedro Jose Pezzarini <jose2190 en gmail.com>:
> Crea un service que solo retorne un csrftoken, y antes de enviar el
> formulario, anexá el token como un input en hidden.
>
> El html te va a quedar cacheado y solo vas a enviar el token anexado.
>
> Espero te sirva mis 2 centavos.
>
> Saludos!
>
>
> El 16 de julio de 2014, 21:10, ken248000 en gmail.com <ken248000 en gmail.com>
> escribió:
>
>> Django carga el token en una cookie llamada "csrftoken",
>> Deberias poder obtenerla con javascript y setear el input csrf de tu form.
>>
>>
>> 2014-07-14 16:19 GMT-03:00 Andres Riancho <andres.riancho en gmail.com>:
>>
>>> Ezequiel,
>>>
>>>     Gracias por la pronta respuesta, tu propuesta de usar AJAX es algo
>>> que querría evitar si es posible, principalmente porque agrega un HTTP
>>> request para la carga del form (o al menos para la carga del token).
>>> Con las opciones que estaba viendo, y referencie en el email original,
>>> todo se cargaría en un solo request HTTP y sin  cambiar nada del
>>> codigo front-end.
>>>
>>>     Entiendo que no existe una solución perfecta y que posiblemente
>>> tenga que ir por este workaround pero... no hay nada mejor?
>>>
>>> Saludos,
>>>
>>> 2014-07-14 16:11 GMT-03:00 Ezequiel Gonzalez Rial <gonrial en gmail.com>:
>>> > Hola Andrés,
>>> >
>>> > Si vas a cachear olvidate del CSRF. La idea de cachear es que no tengas
>>> > carga dinámica. Si tal como estás diciendo lo que necesitas es que
>>> > todas las
>>> > vistas tengan un formulario, entonces lo que te recomiendo es que uses
>>> > AJAX
>>> > para hacer esa parte de la carga. De esa forma, lo estático se carga
>>> > rápido
>>> > y cacheado y después podes aplicar toda la lógica necesaria para que
>>> > ese
>>> > formulario aparezca como tiene que procesarse.
>>> >
>>> > Saludos,
>>> >
>>> > Ezequiel
>>> >
>>> >
>>> > El 14 de julio de 2014, 15:50, Andres Riancho
>>> > <andres.riancho en gmail.com>
>>> > escribió:
>>> >>
>>> >> Lista,
>>> >>
>>> >>     Estuve leyendo bastante sobre como hacer caching del HTML generado
>>> >> por views en Django y los problemas que existen (un gran resumen aqui
>>> >> [0]); llegando a la conclusión principal de que hacer caching bien es
>>> >> dificil ;) y como secundaria que existen distintas maneras de hacerlo:
>>> >>
>>> >>  * Varnish + ESI: No me gusto mucho ya que en las workstations de los
>>> >> devs habría que instalar Varnish, configurarlo, etc.
>>> >>
>>> >>  * Two phase template rendering con cosas como django-phased [1],
>>> >> django-twophase [2] o el partial caching de django-adv-cache-tag [3].
>>> >> Ninguno de estos proyectos, salvo quizás django-adv-cache-tag tienen
>>> >> muchos releases/contribuciones/actividad; por lo que me da la
>>> >> impresión de que no estoy yendo por el camino correcto.
>>> >>
>>> >>     Entonces, que me recomiendan para hacer caching de views en sites
>>> >> con Django? Cual es el mejor approach para los tokens CSRF si por
>>> >> ejemplo tengo un contact form en el footer de todas mis views?
>>> >>
>>> >> [0]
>>> >> https://groups.google.com/forum/#!topic/django-developers/EojHkVKxVWc
>>> >> [1] http://django-phased.readthedocs.org/en/latest/
>>> >> [2] https://launchpad.net/django-twophase
>>> >> [3] http://documentup.com/twidi/django-adv-cache-tag
>>> >>
>>> >> Saludos,
>>> >> --
>>> >> Andrés Riancho
>>> >> Project Leader at w3af - http://w3af.org/
>>> >> Web Application Attack and Audit Framework
>>> >> Twitter: @w3af
>>> >> GPG: 0x93C344F3
>>> >> _______________________________________________
>>> >> pyar mailing list pyar en python.org.ar
>>> >> http://listas.python.org.ar/listinfo/pyar
>>> >>
>>> >> PyAr - Python Argentina - Sitio web: http://www.python.org.ar/
>>> >>
>>> >> La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
>>> >> Argentina - http://www.usla.org.ar
>>> >
>>> >
>>> >
>>> > _______________________________________________
>>> > pyar mailing list pyar en python.org.ar
>>> > http://listas.python.org.ar/listinfo/pyar
>>> >
>>> > PyAr - Python Argentina - Sitio web: http://www.python.org.ar/
>>> >
>>> > La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
>>> > Argentina - http://www.usla.org.ar
>>>
>>>
>>>
>>> --
>>> Andrés Riancho
>>> Project Leader at w3af - http://w3af.org/
>>> Web Application Attack and Audit Framework
>>> Twitter: @w3af
>>> GPG: 0x93C344F3
>>> _______________________________________________
>>> pyar mailing list pyar en python.org.ar
>>> http://listas.python.org.ar/listinfo/pyar
>>>
>>> PyAr - Python Argentina - Sitio web: http://www.python.org.ar/
>>>
>>> La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
>>> Argentina - http://www.usla.org.ar
>>
>>
>>
>> _______________________________________________
>> pyar mailing list pyar en python.org.ar
>> http://listas.python.org.ar/listinfo/pyar
>>
>> PyAr - Python Argentina - Sitio web: http://www.python.org.ar/
>>
>> La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
>> Argentina - http://www.usla.org.ar
>
>
>
> _______________________________________________
> pyar mailing list pyar en python.org.ar
> http://listas.python.org.ar/listinfo/pyar
>
> PyAr - Python Argentina - Sitio web: http://www.python.org.ar/
>
> La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
> Argentina - http://www.usla.org.ar



-- 
Andrés Riancho
Project Leader at w3af - http://w3af.org/
Web Application Attack and Audit Framework
Twitter: @w3af
GPG: 0x93C344F3


More information about the pyar mailing list