Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Ponowne uwierzytelnianie i obrona przed CSRF
Forum PHP.pl > Forum > PHP
WebCM
Istnieją 2 metody zabezpieczeń przed CSRF, które chronią tylko częściowo:
  1. Sprawdzanie nagłówka Referer - niektórzy w trosce o prywatność go wyłączają
  2. Wysyłanie akcji metodą POST - wtedy nie zadziała metoda "na obrazek"

Skuteczniejsze metody:
  1. Jednorazowy klucz w ukrytym polu formularza - musi się zgadzać z zapisanym na serwerze
  2. Potwierdzanie hasła - np. przy wejściu do panelu admina, ważnych zmianach
  3. Kody jednorazowe, SMS, telefon - to już krytyczny poziom zabezpieczeń

Jakie jeszcze znacie skuteczne sposoby zabezpieczeń?

Chcę zastosować takie zabezpieczenia, aby w jak najmniejszym stopniu utrudnić korzystanie z serwisu. Rozważmy jednorazowy klucz. Standardowa implementacja wygląda tak:
  1. if( wyslano_formularz )
  2. {
  3. if( empty($_SESSION['authKey']) OR $_POST['authKey'] != $_SESSION['authKey'] )
  4. {
  5. echo 'Wyślij formularz ponownie';
  6. }
  7. }
  8.  
  9. $_SESSION['authKey'] = uniqid();

Osoba pisze jednocześnie 2 posty. Kiedy wyśle drugi, zobaczy komunikat "Wyślij formularz ponownie". Przyczyna: w sesji zapisujemy klucz pod identyczną nazwą. Innym razem pisze długi post. Znowu do samo. Przyczyna: Sesja wygasa po 20 minutach (można zwiększyć, tylko niezalecane).

Rozważmy ponowne logowanie przy wejściu do panelu admina. Redaktor pisze długi artykuł. Zapisuje. Niestety, sesja wygasła. Musi zalogować się ponownie. Artykuł znika. Zamiast sesji można wykorzystać ciasteczka. Dopóki nie zamknie przeglądarki, nie zostanie wylogowany. Jak nie popełnić błędu? Wystarczy zapisać md5(hasło + coś tam)?

Jak projektować zabezpieczenia, aby nie utrudniać życia użytkownikom?
marcio
Poprostu przed CSRF stosuj token i ewentualnie sesje z czasem np dla dodania komentarza dasz max 30sec, to takie przypuszczenie.

Co do uwierzytelnienia stosuj hash dla kazdego uzytkownika przy jego logowaniu zapisuj go do sesji i poprostu sprawdzaj czy user z takim hashem istnieje w bazie jesli tak porownaj jego nazwe uzytkownika,browser i ip.

Proste ale skuteczne nie ma bata ze wejsc na czyjes konto nie znajac jego hashu co jest prawie niemozliwe!

Cytat
Jakie jeszcze znacie skuteczne sposoby zabezpieczeń?

Po co ci inne jak taki token w zypelnosci wystarcza...?!?
fr33d0m
@marcio
To rozwiązanie z hashem jest raczej średnie - uparty napastnik będzie znał ofiarę (ip,user,browser) i wierzę, że wpadnie na pomysł z hashem.

@WebCM
Zdecydowanie lepsza i wydajniejsza metoda jest z ukrytym kluczem generowanym jednorazowo przy logowaniu i sprawdzaniu później czy się zgadza tym z sesji - tak jak zademonstrowałeś na przykładzie, jest to metoda w zupełności wystarczalna.
marcio
Cytat
To rozwiązanie z hashem jest raczej średnie - uparty napastnik będzie znał ofiarę (ip,user,browser) i wierzę, że wpadnie na pomysł z hashem.

Ciekawy jak bys chcial poznac hash uzytkownika...?!?
Jedynym sposobem bylby sql injection no ale sry jesli juz decydujesz sie na takie zabezpieczenia to sql injection trzeba miec opanowane.

Cytat
Zdecydowanie lepsza i wydajniejsza metoda jest z ukrytym kluczem generowanym jednorazowo przy logowaniu i sprawdzaniu później czy się zgadza tym z sesji - tak jak zademonstrowałeś na przykładzie, jest to metoda w zupełności wystarczalna.

I po co ten zabieg?Taka sama metoda jak i moja niepotrzbnie tylko to robic za kazdym razem jak mozna miec jeden hash na zawsze.
fr33d0m
@marcio
Po pierwsze poprzez 'hash' rozumiem przepuszczony przez np. md5 nazwę usera/agenta/ip/etc - to tak dla jasności dyskusji.
Poznać jest go bardzo łatwo, wystarczy po testować na samym sobie i wierz mi, że nawet nie zbyt uparty napastnik do tego dojdzie, bo nie jest to skomplikowany pomysł obrony. Dla jasności w tej kwestii bardzo łatwo poznać usera,browser,ip wystarczy umiejętnie wykorzystać XSS'a na danym portalu. (nie powinno się zakładać, że takiego nie będzie). To co mnie jeszcze irytuje, to że jak sam napisałeś hash jest jeden na zawsze przypisany do danego konta - jeśli pozna się mechanizm tworzenia hasha to cały portal leży otworem dla CSRF. Przy generowaniu klucza jednorazowo tego problemu nie ma, a nawet więcej... napastnik przy sqlinfect(który przecież też może wystąpić) nie jest wstanie poznać autkeya.

marcio
Cytat
Po pierwsze poprzez 'hash' rozumiem przepuszczony przez np. md5 nazwę usera/agenta/ip/etc

Z oczywistych powodow przez ciebie podanych taki hash powinien sie skladac z danych ktore nie maja jako tako nic wspolnego z uzytkownikiem, dla scislosci:
  1. substr(sha1(time().uniqid()), 0, 15);

Teraz powiedz mi niby jakim cudem ktos ma "odgadnac" hash?Jedyny sposob to wlasnie sql injection ewentualnie za pomoca xss jak sie uda smile.gif a jesli jestes taki pewny(w sumie ja tez mojej tezy jednak kazdy moze sie mylic) wejdz na moj blog i spruboj wejsc na jakies konto, zrobie ci jakies testowe z prawami usera wink.gif
fr33d0m
Dla mnie hash to hash (czysty bez dodatków, który przy ataku idzie jako drugi w kolejności). Gdybyś na początku zarzucił takim kodem to chyba oczywiste, że nie miał bym się o co czepiać. wink.gif
Jednak sprawdzanie przy każdym formularzu takiego hashu w bazie przy kilku tysiącach użytkowników JEST wciąż BARDZO zasobożerne w porównaniu do prostego jednokrotnego generowania keya na sesje... nawet więcej niż zasobożerne - jest to bardzo nie wydajne rozwiązanie. Zakładając, że na stronie będzie sqlinfect i xss to oba sposoby są na tym samym poziomie bezpieczeństwa przed CSRF z różnicą wydajności. Dla uprzedzenia Twojej wypowiedzi dodam, że sqlinfecty są tak samo popularne jak xssy.
Pozdro tongue.gif
marcio
Cytat
Dla mnie hash to hash (czysty bez dodatków, który przy ataku idzie jako drugi w kolejności). Gdybyś na początku zarzucił takim kodem to chyba oczywiste, że nie miał bym się o co czepiać.

To sie nie zrozumielismy wink.gif

Cytat
Jednak sprawdzanie przy każdym formularzu takiego hashu w bazie przy kilku tysiącach użytkowników JEST wciąż BARDZO zasobożerne w porównaniu do prostego jednokrotnego generowania keya na sesje... nawet więcej niż zasobożerne - jest to bardzo nie wydajne rozwiązanie.

Hash sprawdzam tylko wtedy gdy sa odpalane jakies wazne akcje w systemie np zarzadzanie wpisami,panel admina czy usuwanie komentarzy, poprostu frontcontroller PA sprawdza przy jego odpaleniu hash, reszta frontcontrollerow i komponentow uwierzytelnia uzytkownika na podstawie ACL'a.

Cytat
Zakładając, że na stronie będzie sqlinfect i xss to oba sposoby są na tym samym poziomie bezpieczeństwa przed CSRF z różnicą wydajności. Dla uprzedzenia Twojej wypowiedzi dodam, że sqlinfecty są tak samo popularne jak xssy.

Wiec jak zawsze trzeba "zakladac" jak najmniej i testowac wlasne strony ;p
fr33d0m
Ja zawsze testuje swój kod na przeróżne zagrożenia, ale mimo pozytywnych wyników wolę być przygotowany na najgorsze. To tak na /oftop/
Cytat
Hash sprawdzam tylko wtedy gdy sa odpalane jakies wazne akcje w systemie np zarzadzanie wpisami,panel admina czy usuwanie komentarzy, poprostu frontcontroller PA sprawdza przy jego odpaleniu hash, reszta frontcontrollerow i komponentow uwierzytelnia uzytkownika na podstawie ACL'a.

Pamiętaj, że temat nie dotyczy Twojej strony, tylko obrony przed tym rodzajem ataku na użytkowników np. w serwisie społecznościowym gdzie trzeba zabezpieczyć różnego rodzaju formularze, aby ktoś złośliwy z zewnątrz nie napisał cUrlowego robaka. Pomijam już fakt, że ważne operacje dla konta usera powinno zatwierdzać się hasłem.
em1X
Nie trzeba niczego do bazy dodawać. Przy każdym otworzeniu formularza przez użytkownika DOPISZ hash do sesji użytkownika, a po wysłaniu zwyczajnie go usuń z listy. Może otworzyć 10 formularzy, będzie miał w sesji 10 hashy do każdego inny.
Crozin
Cytat
Osoba pisze jednocześnie 2 posty. Kiedy wyśle drugi, zobaczy komunikat "Wyślij formularz ponownie". Przyczyna: w sesji zapisujemy klucz pod identyczną nazwą.
Rozwiązanie: dla każdego formularza generuj unikalny klucz (identyfikjący formularz) oraz token (potwierdzający, że został on wysłany z Twojej strony) - oba losose. W sesji będziesz mieć tablicę {klucz-formularza} => {token}, później jedynie coś w stylu:
  1. if (!isset($_POST['form-token'], $_POST['csrf-token']) || !isset($_SESSION['csrf-tokens'][$_POST['form-token']]) || $_SESSION['csrf-tokens'][$_POST['form-token']] != $_POST['csrf-token']) {
  2. // blah
  3. }
Teraz możesz już mieć otwartych wiele formularzy (nawet tego samego typu).

Cytat
Sesja wygasa po 20 minutach (można zwiększyć, tylko niezalecane).
Przed takim czymś (i innymi problemami - np. nagły zanik prądu u użytkownika) najlepiej chronii okresowe zapisywanie kopii roboczej (szkicu) formularza.
fr33d0m
@Crozin
Szczerze to nie zbyt kumam cały sens Twojego rozwiązania poprzez dwa losowe tokeny. Jeśli agresor ściągnie na swoją stronę, zalogowaną ofiarę gdzie są tokeny(losowe) to już istnieje wysokie prawdopodobieństwo, że zdąży wysłać "coś" w imieniu zalogowanej ofiary i nie ważne czy jest to jeden token czy 20 tokenów z input==hidden, więc po co obciążać sztuczną imitacją ochrony poprzez zwiększenie ilości tokenów?
Crozin
@fr33d0m: Nie, nie będzie mógł wysłać czegoś w imieniu użytkownika bo nie będzie wstanie podać odpowsiedniego klucza.
WebCM
Cytat
Przed takim czymś (i innymi problemami - np. nagły zanik prądu u użytkownika) najlepiej chronii okresowe zapisywanie kopii roboczej (szkicu) formularza.

Zarówno IPB i phpBB zapisują klucz w ukrytym polu formularza. Różnica: po wygaśnięciu sesji phpBB każe wysłać formularz ponownie. IPB przyjmie nawet po kilku godzinach. Czyżby trzymał klucz w bazie? **

Powstał temat bezpiecznego logowania. Załóżmy, że ID użytkownika i hasło w MD5 zapisujemy do ciastek z parametrem Tylko HTTP. Stosując tablice tęczowe, można takie hasło odczytać. Można się zabezpieczyć, dodając klucz: md5(haslo + klucz). Klucz można przypisać każdemu użytkownikowi w bazie (nadmiar danych) lub ustalić 1 wspólny dla wszystkich.

Jakie jest prawdopodobieństwo, że klucz zostanie wykradziony z ciągu znaków md5(hasło+klucz), gdzie "hasło" może być dowolne, co najmniej 5 znaków, natomiast "klucz" jest jeden dla wszystkich (np. 13 znaków)? Od czego to zależy?

** A teraz wyskoczył komunikat "Autoryzacja nie pasuje". Podejrzewam, że niektóre fora IPB mają przedłużoną sesję.
marcio
Cytat
Powstał temat bezpiecznego logowania. Załóżmy, że ID użytkownika i hasło w MD5 zapisujemy do ciastek z parametrem Tylko HTTP. Stosując tablice tęczowe, można takie hasło odczytać. Można się zabezpieczyć, dodając klucz: md5(haslo + klucz). Klucz można przypisać każdemu użytkownikowi w bazie (nadmiar danych) lub ustalić 1 wspólny dla wszystkich.

Po pierwsze zacznij od zmiany md5 na sha1 wink.gif

Po drugie jak masz zapisywac jakis hash to zapisuj go bez zadnych danych uzytkownika nie mieszajac w to zadnych hasel,id,nazw itp...itd...
fr33d0m
Cytat(Crozin @ 23.03.2012, 21:46:19 ) *
@fr33d0m: Nie, nie będzie mógł wysłać czegoś w imieniu użytkownika bo nie będzie wstanie podać odpowsiedniego klucza.

A ja Ci mówię, że się da z przynajmniej 99% skutecznością.
Zalogowany user na podatnym portalu wejdzie na Twoją stronę, gdzie Twój serwer pobierze i przeanalizuje kod źródłowy. Jedyny warunek jest taki, że zalogowany user nie może wejść w trakcie "ataku" na daną podstronę/formularz bo wygeneruje nowy/e token/y. PHP+AJAX/cURL

@WebCM
Prawdopodobieństwo rozszyfrowania klucza zależy przede wszystkim od popularności i tematyki Twojego portalu. To tak jak z algorytmem MD5, który kiedyś był uważany za perfekcyjny, a dziś można poznać większą ilość hashy ze względu na jego popularność i słabe hasła większości internautów. Załóżmy, że ktoś założył u Ciebie 100 kont z różnym hasłem aby przeanalizować jak działa Twoja "sól" stosowana do ciastka - taki cios z pewnością odczuje Twój algorytm, który zdradzi chociażby kilka drobnych informacji na jakiej zasadzie ciastko jest hashowane. Osobiście uważam, że lepiej nie ryzykować z podawaniem hasła w cookies, które można wykraść na wiele sposobów i samemu lub w grupie przeanalizować. Żeby nie było, jestem jak najbardziej za cookiesami i przedłużaniem/autoryzowaniem sesji, ale w sposób, który przedstawił marcio.

Crozin
@fr33d0m: Niby w jaki sposób zewnętrzny serwer miałby uzyskać źródło strony, które jest generowane indywidualnie dla każdej sesji? A słabość MD5 polega na tym, że udało się opracować efektywną metodę dobierania danych wejściowych dla danych danych wyjściowych - popularność nie ma tu wiele do rzeczy.
marcio
Cytat
A słabość MD5 polega na tym, że udało się opracować efektywną metodę dobierania danych wejściowych dla danych danych wyjściowych - popularność nie ma tu wiele do rzeczy.

Rainbow tables i ogolnie bazy gotowych hashowanych hasel, wchodzi podajesz "zakodowane" haslo np w md5 i jesli w bazie znajduje sie "kolizja" zwraca ci haslo.
Jak ktos nie stosuje soli to ma sie 50% szans na zdobycie hasla.
fr33d0m
Cytat
@fr33d0m: Niby w jaki sposób zewnętrzny serwer miałby uzyskać źródło strony, które jest generowane indywidualnie dla każdej sesji? A słabość MD5 polega na tym, że udało się opracować efektywną metodę dobierania danych wejściowych dla danych danych wyjściowych - popularność nie ma tu wiele do rzeczy
A no da się. Nie podam przykładu bo jest to zależne od konstrukcji danego portalu. W taki sposób powstają wredne robaki, które wysyłają za nas spreparowane wiadomości... NK jest podatna, więc jeśli jesteś ciekawy to możesz potrenować. Odnośnie MD5, to nie doszli by do tego, gdyby właśnie nie było to tak popularne i moim zdaniem popularność ma bardzo wiele do rzeczy - chociażby dlatego, że lepiej spędzić miesiąc nad przełamaniem schematu czegoś, co jest częściej stosowane niż spędzić miesiąc co jest stosowane sporadycznie na portalach z minimalną statystyką.
Crozin
@marcio: Każdy algorytm hashujący podatny jest na atak przy użyciu tablic teńczowych, a stosowanie soli jako takiej jest podstawą znaną od początków korzystania z funkcji hashujących, więc może darujemy sobie takie banały.

Cytat
A no da się. Nie podam przykładu bo jest to zależne od konstrukcji danego portalu. W taki sposób powstają wredne robaki, które wysyłają za nas spreparowane wiadomości... NK jest podatna, więc jeśli jesteś ciekawy to możesz potrenować.
Byłbym jednak bardzo wdzięczny za podanie przykładu, bo wg tego co piszesz każdy portal byłby wstanie włamać się na różne moje konta (pocztę, bank, fora itd.) o ile mają odpowiednio złą (tj. jaką?) konstrukcję.
Cytat
Odnośnie MD5, to nie doszli by do tego, gdyby właśnie nie było to tak popularne i moim zdaniem popularność ma bardzo wiele do rzeczy - chociażby dlatego, że lepiej spędzić miesiąc nad przełamaniem schematu czegoś, co jest częściej stosowane niż spędzić miesiąc co jest stosowane sporadycznie na portalach z minimalną statystyką.
To czym się kierowali ludzie nie jest teraz istotne, istotne jest to że opracowali algorytm łamiący podstawową właściowość MD5 - jednokierunkowość. Z SHA1 jest zresztą dosyć podobnie.
fr33d0m
Cytat
A słabość MD5 polega na tym, że udało się opracować efektywną metodę dobierania danych wejściowych dla danych danych wyjściowych - popularność nie ma tu wiele do rzeczy.

Cytat
To czym się kierowali ludzie nie jest teraz istotne, istotne jest to że opracowali algorytm łamiący podstawową właściowość MD5 - jednokierunkowość. Z SHA1 jest zresztą dosyć podobnie.

Kierunek myślenia ludzi jest BARDZO istotny i wchodzą tu aspekty z socjotechniki...tak samo popularność ma bardzo wiele do rzeczy i nie będę dyskutować na ten temat robiąc oftop.

Cytat
Byłbym jednak bardzo wdzięczny za podanie przykładu, bo wg tego co piszesz każdy portal byłby wstanie włamać się na różne moje konta (pocztę, bank, fora itd.) o ile mają odpowiednio złą (tj. jaką?) konstrukcję.

  1. Serwer podantny na CSRF:
  2. if($sesja==true){
  3. pokaz_formularz_z_losowym_tokenem();
  4. }

Nie każdy portal i nie w każdym miejscu bo zależy to od konstrukcji "dojścia" do formularza. Nie podam Ci konkretnego przykładu bo jest to długi temat oparty na metodzie wielu prób i testów. Odnośnie banków, to zauważ, że w większości banków stosują kody jednorazowe lub tokeny przysyłane listem do domu - i nie robią tego tylko po to, aby wydać w kosmos kilkanaście tysięcy/mc.

Obrona przed atakiem CSRF jest trudna, dobrą obroną jest stosowanie rozszerzonych i dokładnych ifów + losowy token dla sesji wykorzystywany dla każdego formularza (jeden token w zupełności wystarczy).


edit do posta poniżej:
Kto powiedział, że serwer musi się podszyć pod sesje użytkownika? dalsza dyskusja nie ma sensu. Po hackuj trochę w celach edukacyjnych to zrozumiesz nieco inny sens pisania kodu niż tylko programistyczny.
Crozin
Cytat
  1. Serwer podantny na CSRF:
  2. if($sesja==true){
  3. pokaz_formularz_z_losowym_tokenem();
  4. }
No i jak to niby miałoby wpłynąć na bezpieczeństwo? Przecież "zły serwer" nie podszyje się pod sesje użytkownika, więc otrzyma swój własny (powiązany z jego sesją) token.
Cytat
Odnośnie banków, to zauważ, że w większości banków stosują kody jednorazowe lub tokeny przysyłane listem do domu - i nie robią tego tylko po to, aby wydać w kosmos kilkanaście tysięcy/mc.
Celem tych kodów nie jest zabezpieczenie przed CSRF.
Cytat
Obrona przed atakiem CSRF jest trudna [...]
Jest to jeden z najpopularniejszych i najprostszych w obronie ataków - na poziomie XSS czy SQL Injection.
Cytat
[...] losowy token dla sesji wykorzystywany dla każdego formularza (jeden token w zupełności wystarczy).
A teraz popatrz na mój pierwszy post i przeczytaj dlaczego zaproponoswałem użycie pary powiązanych.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.