Токены CSRF не соответствуют тому, что находится в сеансе (Rails 4.1)

Мы наблюдаем неприятную и, вероятно, проблему аутентификации токена CSRF на основе браузера в нашем приложении Rails 4.1. Мы публикуем его здесь, чтобы спросить сообщество, видят ли его другие.

Имейте в виду, что большинство инструментов для создания отчетов об ошибках, таких как Honeybadger, автоматически подавляют ActionController::InvalidAuthenticityToken, поэтому вы обычно не видите проблему в своем инструменте для создания отчетов об ошибках, если только не приложите все усилия, чтобы увидеть ее. .

Вот в чем проблема, и это НЕ проблема разработки — это производственная проблема, которую еще предстоит диагностировать.

Исключением является просто ActionController::InvalidAuthenticityToken при обычном входе на наш сайт. После тщательного изучения authenticity_token, отправленного формой, и _csrf_token сеанса (мы используем active_record_store в качестве параметра session_store), они просто не совпадение. При непосредственном рассмотрении могу сделать вывод только, что это совершенно разные токены, но не знаю почему.

Это не простой вопрос разработчика-новичка, пожалуйста, НЕ отвечайте простыми ответами о том, как токен CSRF должен быть передан от клиента к серверу или как обойти защиту от подделки на моих контроллерах. Мне неинтересно слышать от кого-либо один из этих двух ответов: Вы не знаете, о чем говорите, и не понимаете глубины и сложности вопроса. Мне просто интересно Услышав от людей с веб-сайтами с высоким трафиком, которые могут подтвердить, что это происходит с незначительным числом посетителей (и, как ни странно, это влияет на определенные браузеры чаще, чем на другие браузеры).

Мы видим эту проблему широко, может быть, около 1-2% нашего веб-сайта с высокой посещаемостью. Я вижу это только в продакшене, я не могу воспроизвести его в разработке вообще.

Я чаще всего вижу это в браузерах IE 11 и Edge (вы заметите, что Rails 4.1 был выпущен до IE 11 и Edge), но также и в Chrome на Android, а иногда и в мобильном Safari.

Наши заголовки Cache-control установлены следующим образом:

Cache-Control: max-age=0, private, must-revalidate


person Jason FB    schedule 26.07.2017    source источник
comment
Я видел много таких ошибок в нашем приложении, особенно после обновления 3.2 -> 4.1. Мы используем хранилище файлов cookie, а не активную запись. Тем не менее, оба подхода используют куки, у которых может быть срок действия, что и было корнем проблемы в нашем случае: в браузере пользователя была устаревшая форма, ссылающаяся на просроченный куки сеанса. Мы видели намного больше этих ошибок на мобильных устройствах из-за того, как Android и Safari кэшировали фоновые страницы.   -  person Aaron Breckenridge    schedule 26.07.2017
comment
Было ли это решено в Rails 5? Действительно, то, что вы сказали выше, звучит правильно (браузеры удерживают кэшированные страницы). Однако это означает, что браузер не учитывает наш заголовок Cache-control, который явно задан как Cache-Control:no-cache, no-store, max-age=0, must-revalidate.   -  person Jason FB    schedule 07.08.2017
comment
На самом деле позвольте мне исправить это, на странице входа, где мы видим это чаще всего, заголовки управления кешем имеют вид Cache-Control: max-age=0, private, must-revalidate.   -  person Jason FB    schedule 07.08.2017
comment
Насколько я помню, некоторые мобильные устройства не подчинялись заголовкам управления кешем, самой большой проблемой была iOS, но иногда и Android. Вам может не понравиться это решение, но в итоге мы обошли проверку токена подлинности при входе в систему.   -  person Aaron Breckenridge    schedule 07.08.2017
comment
Документация по разработке ссылается на разницу в поведении в Rails 5 ">github.com/plataformatec/devise/blob/. Хотя опыта в разработке у меня мало.   -  person Aaron Breckenridge    schedule 07.08.2017
comment
Я собираюсь изменить весь веб-сайт на Cache-Control: no-cache, no-store, max-age=0, must-revalidate, а также Pragma: no-cache (который должен быть только для браузеров HTTP1.0, но якобы все еще нужен, чтобы заставить современные браузеры не кешировать). Разверну эти исправления и опубликую здесь снова, если это сработает. Обратите внимание, что текущие заголовки на страницах — это Cache-Control: max-age=0, private, must-revalidate без заголовка Pragma.   -  person Jason FB    schedule 08.08.2017
comment
спасибо за вашу помощь @AaronBreckenridge!   -  person Jason FB    schedule 08.08.2017


Ответы (1)


Это выявлено и исправлено. Заголовки управления кешем не были установлены в нашем приложении Rails 4.1, поэтому заголовки по умолчанию

Cache-Control: max-age=0, private, must-revalidate

Этот заголовок недостаточно силен, чтобы заставить браузеры не кэшировать. Таким образом, форма входа и токен JSON кэшировались клиентским браузером — особенно мобильными клиентами — и возвращали идентификаторы сеансов с истекшим сроком действия.

Чинить:

Установите контроль кеша и заголовок прагмы как таковой

Cache-Control:no-cache, no-store, max-age=0, must-revalidate

а также

Pragma: no-cache

В рельсах добавьте это в ваш application_controller.rb :

before_action :set_cache_headers
def set_cache_headers
  response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
  response.headers["Pragma"] = "no-cache"
  response.headers["Expires"] = "Mon, 01 Jan 1990 00:00:00 GMT"
end

Должен ли он быть глобальным для каждого действия в вашем приложении? Это зависит от вас, но вы определенно захотите сделать это на любом контроллере, который отображает форму, особенно форму входа, или на любой странице, которая отображает токен JSON, срок действия которого может истечь. Итак, в современных приложениях короткий ответ — да.

Если вы явно хотите сохранить кэшированные ответы вашего приложения Rails, вам нужно выяснить, как явно истечь срок действия этих токенов CSRF и JSON, если они встроены.

Обратите внимание, что симптом проявляется на малозаметных уровнях появления в основном на мобильных клиентах.


Я исследовал это в сообщении в блоге здесь, посетите мой блог и рассмотрите возможность оставить там комментарий для обсуждения: https://blog.jasonfleetwoodboldt.com/2017/09/03/the-great-rails-cache-lie/

person Jason FB    schedule 14.08.2017
comment
Для всех, кто наткнулся на это как на полезный ответ, стоит понять основу для этого. Согласно этой теме: github.com/rails/rails/issues/21948 мобильные клиенты часто убьет вкладки, чтобы освободить память, которые затем станут открытыми и доступными для отправки, но потеряют соответствующий файл cookie, чтобы соответствовать токену CSRF. Полное уничтожение кэша помогает, если производительность сайта без кэширования не страдает. - person Phil; 25.01.2018
comment
Фил, большое спасибо, никогда не знал об убийстве табов. Я думаю, что кеширование на стороне браузера для большинства динамических веб-страниц в 2018 году — это пережиток прошлого. То есть кэшируйте изображения, JS, CSS и т. д., но зачем кэшировать динамическую веб-страницу, которая будет меняться каждую неделю? Проблема в архаичном представлении о том, что кеширование в браузере — это хорошо. Именно так интернет работал в 2008 году, но не в 2018. Я рекомендую обслуживать небольшие быстрые страницы из Rails (без кэширования), а затем получать все остальное из CDN (кэширование). Это быстро и эффективно. - person Jason FB; 07.02.2018