web-dev-qa-db-fra.com

Sécurité d'utilisation de Thread.current [] dans rails

Je continue à obtenir des opinions contradictoires sur la pratique du stockage d'informations dans le Thread.current hachage (par exemple, l'utilisateur actuel, le sous-domaine actuel, etc.). La technique a été proposée comme un moyen de simplifier le traitement ultérieur au sein de la couche modèle (portée de requête, audit, etc.).

Beaucoup considèrent la pratique inacceptable car elle rompt le modèle MVC. D'autres expriment des préoccupations quant à la fiabilité/sécurité de l'approche, et ma question en deux parties se concentre sur ce dernier aspect.

  1. Est le Thread.current hash garanti d'être disponible et privé pour une et une seule réponse, tout au long de son cycle?

  2. Je comprends qu'un thread, à la fin d'une réponse, pourrait bien être remis à d'autres demandes entrantes, ce qui ferait fuir toute information stockée dans Thread.current. Voudrait effacer ces informations avant la fin de la réponse (par exemple en exécutant Thread.current[:user] = nil d'un contrôleur after_filter) suffit-il pour empêcher une telle atteinte à la sécurité?

Merci! Giuseppe

75
Giuseppe

Il n'y a pas de raison spécifique de rester à l'écart des variables locales de thread, les principaux problèmes sont les suivants:

  • il est plus difficile de les tester, car vous devrez vous rappeler de définir les variables locales du thread lorsque vous testez le code qui l'utilise
  • les classes qui utilisent des threads locaux auront besoin de savoir que ces objets ne sont pas disponibles mais à l'intérieur d'une variable locale de thread et ce type d'indirection rompt généralement le - loi de déméter
  • ne pas nettoyer les threads locaux pourrait être un problème si votre framework réutilise les threads (la variable thread locale serait déjà lancée et le code qui s'appuie sur || = les appels pour initialiser les variables peuvent échouer

Donc, bien qu'il ne soit pas complètement hors de question d'utiliser, la meilleure approche n'est pas de les utiliser, mais de temps en temps vous heurtez un mur où un le thread local va être la solution la plus simple possible sans changer beaucoup de code et vous devrez faire des compromis, avoir un modèle orienté objet moins que parfait avec le thread local ou changer pas mal de code pour faire de même.

Donc, c'est surtout une question de réflexion qui va être la meilleure solution pour votre cas et si vous suivez vraiment le chemin du thread local, je vous conseillerais sûrement de le faire avec des blocs qui se souviennent de nettoyer après ils sont faits, comme suit:

around_filter :do_with_current_user

def do_with_current_user
    Thread.current[:current_user] = self.current_user
    begin
        yield
    ensure
        Thread.current[:current_user] = nil
    end      
end

Cela garantit que la variable locale du thread est nettoyée avant d'être utilisée si ce thread est recyclé.

45
Maurício Linhares

Ce petit bijou garantit que vos variables locales de thread/requête ne collent pas entre les requêtes: https://github.com/steveklabnik/request_store

21
Dejan Simic

La réponse acceptée couvre la question mais comme Rails 5 fournit maintenant une "super classe abstraite" ActiveSupport :: CurrentAttributes qui utilise Thread.current.

J'ai pensé que je fournirais un lien vers cela comme une solution possible ( impopulaire ).

https://github.com/Rails/rails/blob/master/activesupport/lib/active_support/current_attributes.rb

9
Jonny

La réponse acceptée est techniquement exacte, mais comme indiqué dans la réponse doucement et dans http://m.onkey.org/thread-safety-for-your-Rails pas si doucement:

N'utilisez pas le stockage local des threads, Thread.current si vous n'êtes pas obligé

Le bijou pour request_store est une autre solution (meilleure) mais lisez simplement le fichier Lisez-moi pour plus de raisons de ne pas utiliser le stockage local des threads.

Il y a presque toujours une meilleure façon.

3
Tom Harrison