2016-11-11 7 views
12

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 
+0

Jakiego rodzaju "session_store" używasz? –

Odpowiedz

8

więc co sesja wygasa?

Sesja w pytaniu jest Rails Session (patrz What are Sessions? w Ruby on Rails Security Guide), która jest lekka abstrakcja danych, przechowywania, które utrzymuje dowolny stan całej żądań HTTP (w zaszyfrowanym pliku cookie domyślnie innych implementacjach sesji magazynowych można również skonfigurować). Stan sesji może zawierać uwierzytelniony identyfikator użytkownika, ale może być również używany do innych celów.

W tym przypadku sesja nie jest używana do uwierzytelniania użytkownika, ale do przechowywania tymczasowego "tokena uwierzytelnienia" w ramach funkcji bezpieczeństwa Railsów, która chroni witrynę przed atakami Cross-Site Request Forgery (CSRF) na POST (lub innych -GET).

Rails API documentation opisuje tę funkcję w sposób bardziej szczegółowy: działania

Controller są chronione przed Cross-Site Prośba Fałszerstwo (CSRF) ataki tym token w wytopione HTML dla danej aplikacji. Ten token jest zapisywany jako losowy ciąg w sesji, do którego atakujący nie ma dostępu. Gdy żądanie dotrze do twojej aplikacji, Railsy weryfikują otrzymany token z tokenem w sesji.

Zobacz także sekcję Cross-Site Request Forgery z Ruby on Rails Security Guide dla pełniejszego przeglądu co atak CSRF jest i jak weryfikacja autentyczności token sesji opartej chroni przed nim.

Jakie są inne opcje rozwiązania tego problemu?

  • Zwiększ czas trwania sesji Rails czasu wygaśnięcia (na przykład poprzez zwiększenie czasu trwania opcji expire_after przekazany do cookie_store inicjatora lub usuwając opcję całkowicie, aby sesja nigdy nie wygaśnie).

  • Zamiast wygasania sesji cookie wygasają sesje zalogowanych-in, należy Devise „s :timeoutable moduł:

    devise :timeoutable, timeout_in: 15.minutes 
    

Albo jest rozwiązaniem użyliśmy nie tak niepewnie jak my myśleć?

Konfiguracja Rails pominąć verify_authenticity_token zwrotnego wyłącza ochronę CSRF dla danego działania kontrolera, która sprawia, że ​​działania na ataki CSRF.

Więc przeformułować pytanie, jest wyłączenie ochrony CSRF tylkodla działania SessionsController#create nadal niepewny w jakimkolwiek znaczącym/znaczącego sensie? Chociaż to zależy od twojej aplikacji, ogólnie tak, tak sądzę.

Rozważmy następujący scenariusz:

atak CSRF SessionsController#create pozwoliłaby atakującemu złośliwie skierować przeglądarkę ofiary, aby zalogować się do konta użytkownika pod kontrolą atakującego. Następnym razem, gdy ofiara odwiedzi Twoją witrynę (np. Przekierowana przez atakującego), ich przeglądarka może być nadal zalogowana na konto kontrolowane przez osobę atakującą. Ofiara może następnie bezwiednie przesłać poufne dane osobowe lub wykonać poufne działania na twojej stronie internetowej, które mogą zostać odczytane przez osobę atakującą.

Chociaż ten scenariusz może być mniej ewidentnie niebezpieczny niż to, co może się zdarzyć, jeśli ochrona CSRF została wyłączona w innych bardziej czułych/destrukcyjnych działaniach kontrolera, nadal ujawnia wystarczającą ilość potencjalnego problemu dotyczącego prywatności użytkowników/danych, który uważam za niepewny. wystarczy polecić przeciwko wyłączenie domyślnej ochrony.

+0

Świetna odpowiedź, dzięki! Wygląda na to, że jest to trochę kompromisem ... albo wyłącz token i otwórz się na atak CSRF, albo przedłużaj/wyłączaj wygasanie sesji, ale potem otwórz się na inne problemy z bezpieczeństwem. – Cameron

+0

Jednym z rozwiązań było ręczne niszczenie sesji dla auth, ale pozwoliliśmy innym sesjom trwać w nieskończoność. Zobacz zaktualizowany post. – Cameron

+1

@Cameron, jeśli przedłużysz okres ważności pliku cookie sesji, powinieneś być w stanie użyć modułu 'timeoutable' firmy Devise do wygaśnięcia zalogowanych sesji bez dodatkowego kodu:' devise: timeoutable, timeout_in: 15.minutes'. – wjordan

1
InvalidAuthenticityToken 

Ten błąd pojawia się, gdy "token autentyczności" utworzony podczas renderowania formularza (formularz logowania) już wygasł.

Są tylko dwa sposoby rozwiązania tego problemu.

  • Konfiguracja Rails pominąć verify_authenticity_token dla kontrolera # akcji - Niepewne jak już wspomniano w pytaniu
  • (tylko w lewym sposób) Auto odświeżyć ekran (tutaj Ekran logowania) ilekroć napotkał taki wyjątek.

Znajdź kod zmodyfikowane do tego samego i dodać to w application_controller.rb uogólniać rozwiązanie dla całej aplikacji.

rescue_from ActionController::InvalidAuthenticityToken do 
    redirect_to root_url 
end 
1

Inną opcją byłoby to niektóre JS na stronie, która automatycznie przeładować chwilę przed wygaśnięciem tokenu (a zatem zachować token uwierzytelniający prawidłowy).

Najprawdopodobniej będziesz chciał sprawdzić działanie myszy/klawiatury przed ponownym załadowaniem w przypadku, gdy użytkownik właśnie wypełnia formularz.

Wydaje się, że największą wadą tej opcji jest fakt, że JS jest oddzielony od faktycznego czasu ważności tokenu uwierzytelnionego ... Tak więc może to okazać się problem z konserwacją, jeśli zmienisz limit czasu często.

Powiązane problemy