web-dev-qa-db-ja.com

Django悪いのにスレッドローカルを使用するのはなぜですか?

スレッドローカルを使用して、現在のユーザーとリクエストオブジェクトを保存しています。このようにして、プログラム内のどこからでも(動的フォームなど)リクエストを渡すことなく簡単にアクセスできます。

ミドルウェアにスレッドローカルストレージを実装するために、Djangoサイト: http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser)のチュートリアルに従いました。 ?version = 18

その後、このドキュメントは、この手法を回避することを提案するように変更されました: http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser?version=20

記事から:

設計の観点から、threadlocalsは本質的にグローバル変数であり、グローバル変数が通常伴う移植性と予測可能性のすべての通常の問題の影響を受けます。

さらに重要なことに、セキュリティの観点から、threadlocalsは大きなリスクをもたらします。他のスレッドの状態を公開するデータストアを提供することにより、Webサーバー内の1つのスレッドが、システム内の別のスレッドの状態を変更する可能性がある方法を提供します。スレッドローカルデータにユーザーの説明やその他の認証関連データが含まれている場合、そのデータは、許可されていないユーザーへのアクセスを許可したり、ユーザーの個人情報を公開したりする攻撃の基礎として使用される可能性があります。この種の攻撃から安全なスレッドローカルシステムを構築することは可能ですが、防御的になり、そもそもそのような脆弱性の影響を受けないシステムを構築する方がはるかに簡単です。

グローバル変数が悪い理由は理解できますが、この場合は自分のサーバーで自分のコードを実行しているため、2つのグローバル変数がどのような危険をもたらすのかわかりません。

誰かが関連するセキュリティ問題を説明できますか?この記事を読んで、私がスレッドローカルを使用していることを知っている場合、どのようにアプリケーションをハッキングするかを多くの人に尋ねましたが、誰も私に言うことができませんでした。これは、オブジェクトを明示的に渡すのが大好きな髪を分割する純粋主義者の意見だと私は考え始めています。

46
hekevintran

私は完全に同意しません。 TLSは非常に便利です。グローバルを注意して使用するのと同じように、注意して使用する必要があります。しかし、それをまったく使用すべきではないと言うことは、グローバルを決して使用すべきではないと言うのと同じくらいばかげています。

たとえば、現在アクティブなリクエストをTLSに保存します。これにより、すべてのインターフェースを介してリクエストを渡すことなく、ロギングクラスからアクセスできるようになります。これには、Djangoをまったく気にしない多くのインターフェースも含まれます。これにより、からログエントリを作成できます。コード内の任意の場所。ロガーはデータベーステーブルに出力し、ログが作成されたときに要求がアクティブである場合、アクティブなユーザーや要求されていたものなどをログに記録します。

あるスレッドに別のスレッドのTLSデータを変更する機能を持たせたくない場合は、これを禁止するようにTLSを設定します。これには、おそらくネイティブTLSクラスを使用する必要があります。しかし、その議論には説得力がありません。攻撃者がバックエンドとして任意のPythonコードを実行できる場合、システムはすでに致命的に侵害されています。たとえば、後で別のユーザーとして実行するために、モンキーパッチを適用する可能性があります。

もちろん、リクエストの最後にTLSをクリアする必要があります。 Djangoでは、ミドルウェアクラスのprocess_responseとprocess_exceptionでクリアすることを意味します。

46
Glenn Maynard

最新のDjango 1.10と互換性のあるTLSミドルウェアを作成する方法の簡単な例:

# 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
alexpirine

異なるユーザーからのデータを混同する可能性があるという事実にもかかわらず、スレッドローカルは依存関係を隠すため、避ける必要があります。メソッドに引数を渡すと、何を渡しているかがわかります。しかし、ローカルスレッドはバックグラウンドの隠しチャネルのようなものであり、メソッドが正しく機能しない場合があるのではないかと思うかもしれません。

スレッドローカルが良い選択である場合もありますが、それらはめったに注意深く使用されるべきではありません!

12
deamon