В нашем 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