2010-07-12 25 views
42

Estoy usando subprocesos locales para almacenar el usuario actual y solicitar objetos. De esta forma, puedo acceder fácilmente a la solicitud desde cualquier parte del programa (por ejemplo, formularios dinámicos) sin tener que pasarlos por alto.¿Por qué es malo usar locales de subprocesos en Django?

Para implementar el almacenamiento de los locales de rosca en un middleware, he seguido un tutorial en el sitio Django: http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser?version=18

Este documento ya ha sido modificado para sugerir evitar esta técnica: http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser?version=20

Desde el artículo :

Desde el punto de vista del diseño, los threadlocals son variables esencialmente globales, y están sujetos a todos los problemas habituales de portabilidad y predictabi lidad que generalmente implican las variables globales.

Más importante aún, desde el punto de vista de la seguridad, los hilos de rosca representan un gran riesgo. Al proporcionar un almacén de datos que expone el estado de otros hilos, proporciona una forma de que un hilo en su servidor web modifique potencialmente el estado de otro hilo en el sistema. Si los datos locales de thread contienen descripciones de usuarios u otros datos relacionados con la autenticación, esos datos podrían usarse como base para un ataque que otorgue acceso a un usuario no autorizado o exponer detalles privados de un usuario. Si bien es posible construir un sistema threadlocal que esté a salvo de este tipo de ataque, es mucho más fácil estar a la defensiva y construir un sistema que no esté sujeto a tal vulnerabilidad en primer lugar.

Entiendo por qué las variables globales pueden ser malas, pero en este caso estoy ejecutando mi propio código en mi propio servidor, por lo que no puedo ver el peligro que representan dos variables globales.

¿Alguien puede explicar el problema de seguridad involucrado? Le he preguntado a muchas personas cómo podrían hackear mi aplicación si leen este artículo y saben que estoy usando conversaciones locales, sin embargo, nadie ha podido decirme. Estoy empezando a sospechar que esta es una opinión sostenida por los puristas que dividen el cabello que adoran pasar los objetos explícitamente.

+2

Por cierto - ¿tienes el ejemplo original? Se borró ahora y quiero usar eso ... – rslite

+2

Este fragmento es bastante similar a la página eliminada: http://djangosnippets.org/snippets/2179/ – hekevintran

+0

Middleware GlobalRequest: https://djangosnippets.org/snippets/2853 –

Respuesta

39

Estoy totalmente en desacuerdo. TLS es extremadamente útil. Se debe usar con cuidado, al igual que los globals se deben usar con cuidado; pero decir que no debería usarse en absoluto es tan ridículo como decir que los globales nunca deben usarse.

Por ejemplo, almaceno la solicitud actualmente activa en TLS. Esto hace que sea accesible desde mi clase de registro, sin tener que pasar la solicitud a través de cada interfaz, incluidos muchos que no se preocupan por Django en absoluto. Me permite hacer entradas de registro desde cualquier parte del código; el registrador da salida a una tabla de base de datos, y si una solicitud pasa a estar activa cuando se realiza un registro, registra cosas como el usuario activo y lo que se solicitó.

Si no desea que un hilo tenga la capacidad de modificar los datos TLS de otro hilo, configure su TLS para prohibirlo, lo que probablemente requiera el uso de una clase TLS nativa.Sin embargo, no encuentro ese argumento convincente; si un atacante puede ejecutar código Python arbitrario como su backend, su sistema ya está fatalmente comprometido; podría parchear cualquier cosa para ejecutarlo más tarde como un usuario diferente, por ejemplo.

Obviamente, querrá borrar cualquier TLS al final de una solicitud; en Django, eso significa borrarlo en process_response y process_exception en una clase de middleware.

+0

Gracias por su confirmación. Ahora me siento menos loco =). Si un atacante puede leer los datos de threadlocal, entonces debe poder SSH en mi máquina de todos modos. – hekevintran

+2

No necesariamente SSH, pero al menos para tener algún tipo de control sobre el back-end de Python. Toda la discusión parece bastante artificial, de todos modos. –

+1

Tengo curiosidad por lo que, exactamente, quiere decir con "borrar cualquier TLS al final de una solicitud". ¿Borrarlo cómo? ¿Eliminar el objeto 'local' en sí, o solo el atributo en el objeto' local' en el que almacenó la sesión? Sin embargo, para ser sincero, ni siquiera estoy seguro de si hay una diferencia relevante entre esos. – CoreDumpError

10

A pesar de que puede mezclar datos de diferentes usuarios, se deben evitar los locales de subprocesos porque ocultan una dependencia. Si pasa argumentos a un método, verá y sabrá lo que está pasando. Pero un hilo local es algo así como un canal oculto en el fondo y se puede preguntar, que un método no funciona correctamente en algunos casos.

Hay algunos casos en que los locales de hilo son una buena opción, ¡pero deben usarse con poca frecuencia y cuidado!

+4

Pero esto no es un problema porque sé que los locales de threads se usan en cualquier lugar al que llamo get_current_user(). Solo tengo dos variables en el hilo locales de todos modos. – hekevintran

9

Un ejemplo rápido de cómo crear un middleware TLS compatible con la última Django 1.10:

# coding: utf-8 
# Copyright (c) Alexandre Syenchuk (alexpirine), 2016 

try: 
    from threading import local 
except ImportError: 
    from django.utils._threading_local import local 

_thread_locals = local() 

def get_current_request(): 
    return getattr(_thread_locals, 'request', None) 

def get_current_user(): 
    request = get_current_request() 
    if request: 
     return getattr(request, 'user', None) 

class ThreadLocalMiddleware(object): 
    def __init__(self, get_response): 
     self.get_response = get_response 

    def __call__(self, request): 
     _thread_locals.request = request 
     return self.get_response(request) 
+1

Esto no funciona cuando se usa UWSGI. 'local()' se comparte –

+0

¿Podría explicarnos más, @JavierBuzzi? ¿Quiere decir que 'local()' se comparte entre subprocesos cuando se usa UWSGI? ¿Tienes una explicación de por qué es eso? – alexpirine

+0

Debido a la forma en que funciona UWSGI, comparte todo entre todos los hilos para acelerar las cosas. https://www.pythonanywhere.com/forums/topic/710/ - es bastante fácil de probar: pip install uwsgi y pruébelo usted mismo. Como nota al margen, tuve que crear un middleware que creara caché en el comienzo de la solicitud y la borramos al final de la solicitud ... solo para estar seguros - de lo contrario estaba recibiendo todo tipo de errores al ejecutarlo dentro de uwsgi –

Cuestiones relacionadas