W naszej aplikacji Rails używamy protect_from_forgery
, aby zapobiec CSRF.Szyny protect_from_forgery łamie formularz logowania, jeśli nie ma go na stanie.
Jednak odkryliśmy, że jeśli użytkownik wejdzie na stronę logowania, a następnie wyskoczy na filiżankę herbaty na czas trwania sesji (czyli powiedzmy 15 minut). Login po prostu przekierowuje z powrotem do strony logowania, niezależnie od udanych poświadczeń lub nie (w zależności od środowiska, otrzymujemy błąd InvalidAuthenticityToken
.) Próba logowania ponownie działa dobrze. Nie powiedzie się tylko, jeśli użytkownik był na stronie dłużej niż czas sesji.
Pomyśleliśmy, że to dziwne, ponieważ jeszcze się nie zalogowaliśmy ... więc jaka sesja wygasa? i na pewno tworzona jest nowa sesja logowania, nawet jeśli została utworzona i straciła ważność. Okazało się (po przeczytaniu tego: https://nvisium.com/blog/2014/09/10/understanding-protectfromforgery/) ochrona CSRF w Railsach faktycznie używa sesji do sprawdzenia, czy authenticity_token
jest poprawna. Zasadniczo token wygasa po wygaśnięciu sesji (w zależności od ustawienia session_store
) i nie można się zalogować bez ponownego odświeżania strony.
Rozwiązaliśmy to: skip_before_action :verify_authenticity_token, only: [:create]
w naszym SessionsController
, ale teraz oznacza to, że nasz formularz logowania nie jest już chroniony.
Jakie są inne opcje rozwiązania tego problemu? A może rozwiązanie, które wykorzystaliśmy, nie jest tak niebezpieczne, jak nam się wydaje? Googling pokazuje tę linię kodu używaną wiele razy, ale z pewnością jest to zły pomysł?
Nasze inne rozwiązanie było umożliwienie Wyjątkiem stało, ale obsługiwać go wdzięcznie z:
rescue_from ActionController::InvalidAuthenticityToken do
@exception = exception.message
render 'errors/500', :status => 500, :layout => 'other'
end
Choć nadal nienawidzić fakt użytkownik siedzi na stronie logowania na dłużej niż limit czasu sesji (w tym przypadku 15 minut) powoduje błąd!
Jeszcze innym rozwiązaniem mamy wymyślić jest ustawienie session_store na zawsze, a następnie ręcznie wygasają sesje logowania tak:
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
Jakiego rodzaju "session_store" używasz? –