Rails protect_from_forgery прерывает регистрационную форму, если в режиме ожидания

В нашем Rails-приложении мы используем protect_from_forgery для предотвращения CSRF.

Однако мы обнаружили, что если пользователь посещает страницу входа в систему, а затем уходит, чтобы выпить чашку чая в течение срока действия сеанса приложения (скажем, 15 минут). Вход просто перенаправляется обратно на страницу входа, независимо от успешных учетных данных или нет (в зависимости от среды мы получаем ошибку InvalidAuthenticityToken.) Повторная попытка входа в систему работает нормально. Это происходит только в том случае, если пользователь находился на странице дольше, чем время сеанса.

Мы думали, что это было странно, потому что мы еще не вошли в систему... так, какая сессия заканчивается? и, конечно, новый сеанс создается при входе в систему, даже если он был создан и истек. Оказывается (после прочтения этого: https://nvisium.com/blog/2014/09/10/understanding-protectfromforgery/) защита CSRF в Rails фактически использует сеанс для проверки authenticity_token. Таким образом, в основном токен истекает, когда сеанс истекает (в зависимости от параметра session_store), и вы не можете войти в систему, не обновляя страницу снова.

Мы решили это сделать: skip_before_action :verify_authenticity_token, only: [:create] в нашем SessionsController, но теперь это означает, что наша форма входа больше не защищена.

Какие еще варианты можно исправить? Или это решение, которое мы использовали не так неуверенно, как мы думаем? Googling показывает эту строку кода много раз, но, конечно, это плохая идея?

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

rescue_from ActionController::InvalidAuthenticityToken do
  @exception = exception.message
  render 'errors/500', :status => 500, :layout => 'other'
end

Хотя он все еще ненавидит тот факт, что пользователь, сидящий на странице входа в систему дольше, чем таймаут сеанса (в этом случае 15 минут), вызывает ошибку!


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

before_action :session_timeout, if: :current_user

def session_timeout
  session[:last_seen_at] ||= Time.now
  if session[:last_seen_at] < 15.minutes.ago
    reset_session
  else
    session[:last_seen_at] = Time.now
  end
end

Ответ 1

что заканчивается сеанс?

Этот сеанс - это сеанс Rails (см. Что такое сеансы? в Руководстве по безопасности Ruby on Rails), которая представляет собой легкую абстракцию хранения данных, которая сохраняется в произвольном состоянии по HTTP-запросам (по умолчанию в зашифрованном файле cookie могут быть также настроены другие реализации хранилища сеансов). Состояние сеанса может включать аутентифицированный идентификатор пользователя, но его также можно использовать для других целей.

В этом случае сеанс не используется для аутентификации пользователя, а для хранения временного токена аутентификации как части функции безопасности Rails, которая защищает ваш сайт от Cross- Атаки запроса на сайт (CSRF) на запросы POST (или другие не-GET).

Документация Rails API описывает эту функцию более подробно:

Действия с контроллером защищены от атак с межсайтовым запросом (CSRF), включая маркер в отображаемом HTML для вашего приложения. Этот токен хранится как случайная строка в сеансе, к которой злоумышленник не имеет доступа. Когда запрос достигает вашего приложения, Rails проверяет полученный токен с токеном в сеансе.

См. также раздел Подпрограмма запроса на использование в Руководстве по безопасности Ruby on Rails для более полный обзор того, что такое CSRF-атака, и то, как защита от проверки подлинности на основе сеанса защищает его.

Какие еще варианты можно исправить?

  • Увеличьте продолжительность истечения срока действия вашего Rails (например, увеличив продолжительность параметра expire_after, переданного на ваш инициализатор cookie_store, или полностью удалив параметр, чтобы сеанс никогда не истекал).

  • Вместо использования сеанса cookie для истечения сеансов входа в систему используйте Devise :timeoutable модуль:

    devise :timeoutable, timeout_in: 15.minutes
    

Или это решение, которое мы использовали не так неуверенно, как думаем?

Настройка Rails для пропуска обратного вызова verify_authenticity_token отключает защиту CSRF для этого конкретного действия контроллера, что делает действие уязвимым для атак CSRF.

Итак, чтобы перефразировать ваш вопрос, отключает защиту CSRF только для действия SessionsController#create по-прежнему небезопасно в любом значимом/значимом смысле? Хотя это зависит от вашего приложения, в общем да, я так считаю.

Рассмотрим этот сценарий:

Атака CSRF против SessionsController#create позволит злоумышленнику злоумышленно направлять браузер-жертву для входа в учетную запись пользователя под контролем злоумышленника. В следующий раз, когда жертва посетила ваш сайт (например, при перенаправлении злоумышленником), их браузер все равно мог войти в контролируемую атакующую учетную запись. Затем жертва могла бессознательно передавать конфиденциальные личные данные или выполнять чувствительные действия на вашем веб-сайте, которые могли быть прочитаны злоумышленником.

Хотя этот сценарий может быть менее явно опасным, чем то, что может произойти, если защита CSRF была отключена на других более чувствительных/деструктивных действиях контроллера, она по-прежнему предоставляет достаточно потенциальную проблему конфиденциальности данных пользователей/данных, которая, по моему мнению, небезопасна рекомендовать против отключения защиты по умолчанию.

Ответ 2

InvalidAuthenticityToken 

Эта ошибка возникает, когда "токен аутентификации", созданный при рендеринге формы (форма входа), уже истек.

Есть только два способа решения этой проблемы.

  • Настройка Rails для пропуска verify_authenticity_token для действия контроллера # - Небезопасно, как уже упоминалось в вопросе
  • (Только влево) Автоматическая перезагрузка экрана (здесь экран входа) при возникновении такого исключения.

Найти измененный код для него и добавить его в application_controller.rb, чтобы обобщить решение для всего приложения.

rescue_from ActionController::InvalidAuthenticityToken do
  redirect_to root_url
end

Ответ 3

Другим вариантом будет включение некоторых JS на странице, которые будут автоматически перезагружаться через некоторое время до истечения срока действия вашего токена (и, следовательно, сохранить действительный токен auth).

Вероятно, вы захотите проверить действие мыши/клавиатуры перед перезагрузкой в ​​случае, если пользователь просто заполняет форму.

Похоже, что самым большим недостатком этой опции будет тот факт, что JS отделен от фактического количества времени, когда токен аутентификации действителен... Таким образом, это может оказаться проблемой обслуживания, если вы измените продолжительность таймаута часто.