Why is using thread locators in Django bad?

I use stream locators to store the current user and request objects. Thus, I can easily access the request from anywhere in the program (for example, dynamic forms) without the need to submit them.

To implement thread locator storage in middleware, I followed a tutorial on the Django website: http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser?version=18

This document has since been modified to suggest avoiding this technique: http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser?version=20

From the article:

From a design point of view, threadlocals are essentially global variables and obey all the usual portability and predictability problems that global variables usually entail.

More importantly, from a security point of view, threadlocals pose a huge risk. By providing a data warehouse that provides the state of other threads, you enable one thread of your web server to potentially change the state of another thread in the system. If threadlocal data contains user descriptions or other authentication-related data, this data can be used as the basis for an attack that provides access to an unauthorized user or provides personal user data. Despite the fact that there is the possibility of creating a threadlocal system that is safe from such attacks, it is much easier to defend and create a system that is not exposed to such a vulnerability in the first place.

I understand why global variables can be bad, but in this case I run my own code on my own server, so I don’t see how dangerous the two global variables are.

Can someone explain the security issue? I asked many people how they would hack my application if they read this article and know that I use stream locators, but no one could tell me. I am beginning to suspect that this opinion is held by picky purists who like to convey objects explicitly.

+46
python django thread-local
Jul 12 '10 at 9:21
source share
3 answers

I do not agree completely. TLS is extremely useful. It should be used with caution, just as global characters should be used with caution; but to say that it should not be used at all is as ridiculous as to say that global symbols should never be used.

For example, I save the current active request in TLS. This makes it accessible from my logging class, without having to pass the request through every single interface, including many that are not interested in Django at all. It allows me to write records from anywhere in the code; logger outputs to the database table, and if the request becomes active when creating the log, it logs things like the active user and what is being requested.

If you do not want one thread to be able to modify the TLS data of another thread, set TLS to disable this, which probably requires the use of its own TLS class. However, I do not consider this argument convincing; if an attacker can execute arbitrary Python code as your backend, your system is already deadly compromised - it can decapitate everything that will be launched later, like another user, for example.

Obviously, you need to clear TLS at the end of the request; in Django, this means flushing it in process_response and process_exception in the middleware class.

+46
Jul 12 2018-10-12T00:
source share

Although you can mix data from different users, local streams should be avoided as they hide the dependency. If you pass arguments to the method that you see and know what you pass. But the local stream is a bit of a hidden channel in the background, and you may be surprised that in some cases the method does not work correctly.

There are times when stream locators are a good choice, but they should be used rarely and carefully!

+12
Jul 12 2018-10-12T00:
source share

A quick example of how to create TLS middleware compatible with the latest 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) 
+12
Sep 04 '16 at 5:22
source share



All Articles