Безпека використання поточного потоку [] в рейках


81

Я постійно отримую суперечливі думки щодо практики зберігання інформації в Thread.currentхеші (наприклад, поточний_користувач, поточний субдомен тощо). Ця техніка була запропонована як спосіб спрощення подальшої обробки в межах рівня моделі (масштабування запитів, аудит тощо).

Багато хто вважає цю практику неприйнятною, оскільки вона порушує схему MVC. Інші висловлюють занепокоєння щодо надійності / безпеки підходу, і моє двоскладове запитання зосереджується на останньому аспекті.

  1. Чи Thread.currentгарантовано хеш буде доступним та приватним для однієї і тієї самої відповіді протягом усього циклу?

  2. Я розумію, що потік в кінці відповіді цілком може бути переданий іншим вхідним запитам, тим самим витікаючи будь-яка інформація, що зберігається в Thread.current. Чи Thread.current[:user] = nilбуде after_filterдостатньо очистити таку інформацію до кінця відповіді (наприклад, виконавши з контролера ) для запобігання такому порушенню безпеки?

Дякую! Джузеппе


9
Перегляньте тут розділ "Забруднення за допомогою Thread.current". m.onkey.org/thread-safety-for-your-rails . Це написав один з авторів Jruby. Код №1 ROR сам використовує Thread.current для I18N та часового поясу. Чи це говорить про його гарантію? №2. Якщо №1 відповідає істині, то цього достатньо.
so_mv

Дякую, я додав посилання.
Джузеппе

3
У публікації, на яку ви посилаєтесь, приклади стосуються виключно рівня контролера, і пропоноване рішення, очевидно, є доречним. Однак я підозрюю, що більшість людей зацікавило б - це чистий спосіб надати доступ до моделей до 1-2 частин інформації, яка зазвичай їм заборонена, без додавання додаткових параметрів до кожного виклику моделей. У цьому відношенні всі ті великі страшні попереджувальні знаки "триматися подалі від Thread.current" без конкретних причин, чому поки що залишали мене непевним. Дякую
Джузеппе

Відповіді:


48

Немає конкретної причини триматися подалі від локальних змінних потоків, головними проблемами є:

  • їх важче протестувати, оскільки вам доведеться пам’ятати про встановлення локальних змінних потоку під час тестування коду, який його використовує
  • класи, які використовують локальні потоки потоків, потребуватимуть знань про те, що ці об'єкти недоступні для них, але всередині змінної локального потоку, і такий тип опосередкованості зазвичай порушує закон деметера
  • не очищення локальних потоків може бути проблемою, якщо ваш фреймворк повторно використовує потоки (локальна змінна потоку буде вже ініційована, а код, який покладається на || = виклики для ініціалізації змінних, може не вдатися

Тож, хоча це не зовсім обговорюване використання, найкраще підхід - не використовувати їх, але час від часу ви потрапляєте у стіну, де локальний потік буде найпростішим можливим рішенням, не змінюючи великої кількості коду та вам доведеться піти на компроміс, мати менш досконалу об’єктно-орієнтовану модель із потоком локальних або змінити досить багато коду, щоб зробити те саме.

Отже, в основному це питання думки, яке буде найкращим рішенням для вашої справи, і якщо ви дійсно рухаєтесь по локальній нитці, я б, безумовно, порадив вам зробити це з блоками, які пам’ятають очистити після вони виконані, як описано нижче:

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

Це гарантує, що локальна змінна потоку буде очищена перед використанням, якщо ця нитка перероблена.


1
Використання навколо фільтра з блоком забезпечення буде неприємним до обробки винятків / реєстрації, якщо ви не дуже обережні. Перегляньте RequestStore у відповіді Деяна щодо більш чистого рішення на основі проміжного програмного забезпечення.
Irongaze.com,

Можливо, мій підхід до проблеми був у тому, що я врятував виняток спочатку (а не просто забезпечив, як зазначено вище). Стек викликів винятку отримував скидання до місця, де я повторно піднімався, псуючи журнал помилок Rails та деінде. Зрештою, я вручну скинув захоплений виняток до файлу журналу, щоб зберегти його, але це було потворно. Використання підходу на основі проміжного програмного забезпечення набагато чистіший IMHO.
Irongaze.com

1
Ось чому в наведеному вище коді є лише ensureблок і він не намагається зловити виняток.
Maurício Linhares

Просто, коли ти це бачиш :)
244,


15

Прийнята відповідь охоплює питання, але оскільки Rails 5 тепер надає "Абстрактний супер клас" ActiveSupport :: CurrentAttributes, який використовує Thread.current.

Я думав, що надам посилання на це як можливе ( непопулярне ) рішення.

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


4

Прийнята відповідь є технічно точною, але, як зазначено у відповіді м’яко, а на http://m.onkey.org/thread-safety-for-your-rails - не так м’яко:

Не використовуйте потокове локальне сховище, Thread.currentякщо вам цього не потрібно

Камінь для request_store- це інше рішення (краще), але просто прочитайте там readme з більшої кількості причин, щоб триматися подалі від локального сховища потоків.

Майже завжди є кращий спосіб.


3
Я використовую Unicorn with Rails Multi-tenant app. Чи можете ви запропонувати приклад "кращого шляху"? Посилання, яке ви розмістили, видає помилку програми. Дякую.
лакостенікодер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.