Подтвердить что ты не робот

Почему использование локаторов потоков в Django плохо?

Я использую локаторы потоков для хранения текущего пользователя и запроса объектов. Таким образом, я могу легко получить доступ к запросу из любой точки программы (например, динамические формы) без необходимости передавать их.

Чтобы реализовать хранилище локаторов потоков в промежуточном программном обеспечении, я последовал за учебником на сайте Django: http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser?version=18

Этот документ с тех пор был изменен, чтобы предложить избегать этой техники: http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser?version=20

Из статьи:

С точки зрения дизайна, threadlocals являются по существу глобальными переменными и подчиняются всем обычным проблемам переносимости и предсказуемости, которые обычно влекут за собой глобальные переменные.

Что еще более важно, с точки зрения безопасности, threadlocals представляют огромный риск. Предоставляя хранилище данных, которое предоставляет состояние других потоков, вы предоставляете возможность одному потоку вашего веб-сервера потенциально изменять состояние другого потока в системе. Если данные threadlocal содержат описания пользователей или другие данные, связанные с проверкой подлинности, эти данные могут использоваться в качестве основы для атаки, которая предоставляет доступ к неавторизованному пользователю или предоставляет личные данные пользователя. Несмотря на то, что существует возможность создания системы threadlocal, которая безопасна от такого рода атак, гораздо легче защищаться и создавать систему, которая не подвержена такой уязвимости в первую очередь.

Я понимаю, почему глобальные переменные могут быть плохими, но в этом случае я запускаю свой собственный код на своем собственном сервере, поэтому я не вижу, какую опасность представляют две глобальные переменные.

Может кто-нибудь объяснить проблему безопасности? Я попросил многих людей, как они будут взломать мое приложение, если они прочитают эту статью и знают, что я использую локаторы потоков, но никто не смог мне сказать. Я начинаю подозревать, что это мнение придерживается разборчивых пуристов, которые любят передавать объекты явно.

4b9b3361

Ответ 1

Я не согласен полностью. TLS чрезвычайно полезен. Его следует использовать с осторожностью, так же, как глобальные символы следует использовать с осторожностью; но говорить, что он не должен использоваться вообще, так же смешно, как сказать, что глобальные символы никогда не должны использоваться.

Например, я сохраняю текущий активный запрос в TLS. Это делает его доступным из моего класса ведения журнала, без необходимости передавать запрос через каждый отдельный интерфейс, включая многие, которые вообще не интересуются Django. Он позволяет мне записывать записи из любого места в коде; выходы регистратора в таблицу базы данных, и если запрос становится активным при создании журнала, он регистрирует такие вещи, как активный пользователь и что запрашивается.

Если вы не хотите, чтобы один поток имел возможность изменять данные TLS другого потока, установите TLS, чтобы запретить это, что, вероятно, требует использования собственного TLS-класса. Однако я не считаю этот аргумент убедительным; если злоумышленник может выполнить произвольный код Python в качестве вашего бэкэнд, ваша система уже смертельно скомпрометирована - он может обезглавить все, что будет запущено позже, как другой пользователь, например.

Очевидно, что вам нужно очистить TLS в конце запроса; в Django, это означает очистку его в process_response и process_exception в классе промежуточного программного обеспечения.

Ответ 2

Несмотря на то, что вы можете смешивать данные от разных пользователей, следует избегать локальных потоков, поскольку они скрывают зависимость. Если вы передадите аргументы методу, который видите и знаете, что вы передаете. Но локальный поток является чем-то вроде скрытого канала в фоновом режиме, и вы можете удивиться, что в некоторых случаях метод работает некорректно.

Есть случаи, когда локаторы потоков являются хорошим выбором, но их следует использовать редко и тщательно!

Ответ 3

Быстрый пример того, как создать промежуточное ПО TLS, совместимое с последним 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)