OK, pozwolę sobie to powiedzieć wprost: jeśli umieszczasz dane użytkownika lub cokolwiek pochodzącego z danych użytkownika w pliku cookie w tym celu, robisz coś źle.
Tam. Powiedziałem to. Teraz możemy przejść do właściwej odpowiedzi.
Pytasz, co jest złego w haszowaniu danych użytkownika? Wszystko sprowadza się do powierzchni ekspozycji i bezpieczeństwa poprzez niejasność.
Wyobraź sobie przez chwilę, że jesteś atakującym. Widzisz zestaw kryptograficznych plików cookie dla zapamiętywania w sesji. Ma 32 znaki. Ojej. To może być MD5 ...
Wyobraźmy sobie również przez chwilę, że znają używany algorytm. Na przykład:
md5(salt+username+ip+salt)
Teraz wszystko, co musi zrobić atakujący, to brutalna siła „soli” (która nie jest tak naprawdę solą, ale więcej o tym później), a teraz może wygenerować wszystkie fałszywe tokeny, jakie chce, z dowolną nazwą użytkownika dla swojego adresu IP! Ale brutalne zmuszanie soli jest trudne, prawda? Absolutnie. Ale współczesne procesory graficzne są w tym wyjątkowo dobre. I jeśli nie użyjesz w nim wystarczającej losowości (uczyń ją wystarczająco dużą), szybko spadnie, a wraz z nią klucze do twojego zamku.
Krótko mówiąc, jedyną rzeczą, która cię chroni, jest sól, która tak naprawdę nie chroni cię tak bardzo, jak myślisz.
Ale poczekaj!
Wszystko to przewidywało, że atakujący zna algorytm! Jeśli jest to tajne i zagmatwane, to jesteś bezpieczny, prawda? ŹLE . Ten sposób myślenia ma nazwę: Bezpieczeństwo poprzez zaciemnienie , które NIGDY nie powinno można polegać.
The Better Way
Lepszym sposobem jest nigdy nie pozwolić, aby informacje użytkownika opuściły serwer, z wyjątkiem identyfikatora.
Gdy użytkownik się zaloguje, wygeneruj duży losowy token (128 do 256 bitów). Dodaj to do tabeli bazy danych, która mapuje token na identyfikator użytkownika, a następnie wyślij go do klienta w pliku cookie.
Co jeśli atakujący zgadnie losowy token innego użytkownika?
Zróbmy tutaj matematykę. Generujemy 128-bitowy losowy token. Oznacza to, że istnieją:
possibilities = 2^128
possibilities = 3.4 * 10^38
Teraz, aby pokazać, jak absurdalnie duża jest ta liczba, wyobraźmy sobie każdy serwer w Internecie (powiedzmy dzisiaj 50 000 000), który próbuje brutalnie wymusić tę liczbę z szybkością 1 000 000 000 na sekundę. W rzeczywistości twoje serwery stopiłyby się pod takim obciążeniem, ale zagrajmy to.
guesses_per_second = servers * guesses
guesses_per_second = 50,000,000 * 1,000,000,000
guesses_per_second = 50,000,000,000,000,000
A więc 50 biliardów domysłów na sekundę. To szybko! Dobrze?
time_to_guess = possibilities / guesses_per_second
time_to_guess = 3.4e38 / 50,000,000,000,000,000
time_to_guess = 6,800,000,000,000,000,000,000
A więc 6,8 sekstylionów sekund ...
Spróbujmy sprowadzić to do bardziej przyjaznych liczb.
215,626,585,489,599 years
Lub nawet lepiej:
47917 times the age of the universe
Tak, to 47917 razy więcej niż wiek wszechświata ...
Zasadniczo nie będzie pęknięty.
Więc by podsumować:
Lepszym podejściem, które zalecam, jest przechowywanie pliku cookie z trzema częściami.
function onLogin($user) {
$token = GenerateRandomToken(); // generate a token, should be 128 - 256 bit
storeTokenForUser($user, $token);
$cookie = $user . ':' . $token;
$mac = hash_hmac('sha256', $cookie, SECRET_KEY);
$cookie .= ':' . $mac;
setcookie('rememberme', $cookie);
}
Następnie, aby sprawdzić poprawność:
function rememberMe() {
$cookie = isset($_COOKIE['rememberme']) ? $_COOKIE['rememberme'] : '';
if ($cookie) {
list ($user, $token, $mac) = explode(':', $cookie);
if (!hash_equals(hash_hmac('sha256', $user . ':' . $token, SECRET_KEY), $mac)) {
return false;
}
$usertoken = fetchTokenByUserName($user);
if (hash_equals($usertoken, $token)) {
logUserIn($user);
}
}
}
Uwaga: Nie używaj tokena lub kombinacji użytkownika i tokena do wyszukiwania rekordu w bazie danych. Zawsze pamiętaj o pobraniu rekordu opartego na użytkowniku i skorzystaj z funkcji porównywania bezpiecznej pod względem czasu, aby później porównać pobrany token. Więcej informacji o atakach czasowych .
Teraz bardzo ważne jest, aby SECRET_KEY
był to kryptograficzny sekret (generowany przez coś podobnego /dev/urandom
i / lub pochodzący z wejścia o wysokiej entropii). Ponadto GenerateRandomToken()
musi być silnym losowym źródłem ( mt_rand()
nie jest wystarczająco silne. Użyj biblioteki, takiej jak RandomLib lub random_compat , lub mcrypt_create_iv()
z DEV_URANDOM
) ...
Ma hash_equals()
to na celu zapobieganie atakom czasowym . Jeśli używasz wersji PHP poniżej PHP 5.6, funkcja hash_equals()
nie jest obsługiwana. W takim przypadku możesz zastąpić hash_equals()
funkcją timingSafeCompare:
/**
* A timing safe equals comparison
*
* To prevent leaking length information, it is important
* that user input is always used as the second parameter.
*
* @param string $safe The internal (safe) value to be checked
* @param string $user The user submitted (unsafe) value
*
* @return boolean True if the two strings are identical.
*/
function timingSafeCompare($safe, $user) {
if (function_exists('hash_equals')) {
return hash_equals($safe, $user); // PHP 5.6
}
// Prevent issues if string length is 0
$safe .= chr(0);
$user .= chr(0);
// mbstring.func_overload can make strlen() return invalid numbers
// when operating on raw binary strings; force an 8bit charset here:
if (function_exists('mb_strlen')) {
$safeLen = mb_strlen($safe, '8bit');
$userLen = mb_strlen($user, '8bit');
} else {
$safeLen = strlen($safe);
$userLen = strlen($user);
}
// Set the result to the difference between the lengths
$result = $safeLen - $userLen;
// Note that we ALWAYS iterate over the user-supplied length
// This is to prevent leaking length information
for ($i = 0; $i < $userLen; $i++) {
// Using % here is a trick to prevent notices
// It's safe, since if the lengths are different
// $result is already non-0
$result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i]));
}
// They are only identical strings if $result is exactly 0...
return $result === 0;
}
Zwykle robię coś takiego:
Oczywiście możesz używać różnych nazw plików cookie itp. Możesz także nieco zmienić zawartość pliku cookie, upewnij się tylko, że nie można go łatwo utworzyć. Możesz na przykład również utworzyć user_salt, gdy użytkownik jest tworzony, a także umieścić go w pliku cookie.
Możesz także użyć sha1 zamiast md5 (lub praktycznie dowolnego algorytmu)
źródło
Wprowadzenie
Twój tytuł „Keep Me Logged In” - najlepsze podejście utrudnia mi określenie, od czego zacząć, ponieważ jeśli szukasz najlepszego podejścia, musisz wziąć pod uwagę następujące kwestie:
Ciasteczka
Pliki cookie są wrażliwe. Pomiędzy typowymi lukami w zabezpieczeniach przeglądarki przed kradzieżą plików cookie a atakami typu cross-site scripting musimy zaakceptować, że pliki cookie nie są bezpieczne. Aby poprawić bezpieczeństwo, musisz pamiętać, że
php
setcookies
ma dodatkowe funkcje, takie jakDefinicje
Proste podejście
Prostym rozwiązaniem byłoby:
Powyższe studium przypadku podsumowuje wszystkie przykłady podane na tej stronie, ale są to wady
Lepsze rozwiązanie
Lepszym rozwiązaniem byłoby
Przykładowy kod
Klasa używana
Testowanie w Firefox i Chrome
Korzyść
Niekorzyść
Szybka naprawa
Wielokrotne podejście do plików cookie
Gdy atakujący ma zamiar ukraść pliki cookie, należy skoncentrować je tylko na określonej stronie internetowej lub domenie, np. przyklad.com
Ale tak naprawdę możesz uwierzytelnić użytkownika z 2 różnych domen ( example.com i fakeaddsite.com ) i sprawić, by wyglądał jak „Reklama Cookie”
Niektóre osoby mogą się zastanawiać, w jaki sposób można użyć 2 różnych plików cookie? Cóż, to możliwe, wyobraź sobie
example.com = localhost
ifakeaddsite.com = 192.168.1.120
. Jeśli przejrzysz pliki cookie, będzie to wyglądać takZ powyższego obrazka
192.168.1.120
HTTP_REFERER
REMOTE_ADDR
Korzyść
Niekorzyść
Poprawa
ajax
źródło
Poprosiłem jeden kąt tego pytania tutaj , a odpowiedzi doprowadzi cię do wszystkich tokenów linki czasowe z plików cookie, których potrzebujesz.
Zasadniczo nie przechowujesz identyfikatora użytkownika w pliku cookie. Przechowujesz jednorazowy token (ogromny ciąg), którego użytkownik używa do pobrania starej sesji logowania. Następnie, aby było naprawdę bezpieczne, musisz podać hasło do ciężkich operacji (takich jak zmiana samego hasła).
źródło
Stary wątek, ale wciąż ważny problem. Zauważyłem kilka dobrych odpowiedzi na temat bezpieczeństwa i unikania używania „bezpieczeństwa przez zaciemnienie”, ale rzeczywiste podane metody techniczne nie były wystarczające w moich oczach. Co muszę powiedzieć, zanim przekażę swoją metodę:
Biorąc to wszystko pod uwagę, istnieją dwa świetne sposoby automatycznego logowania w systemie.
Po pierwsze, tani, łatwy sposób, który stawia na kogoś innego. Jeśli włączysz obsługę witryny w, powiedzmy, na swoim koncie google +, prawdopodobnie masz usprawniony przycisk google +, który zaloguje użytkownika, jeśli jest już zalogowany w google (zrobiłem to tutaj, aby odpowiedzieć na to pytanie, jak zawsze zalogowany w Google). Jeśli chcesz, aby użytkownik logował się automatycznie, jeśli jest już zalogowany za pomocą zaufanego i obsługiwanego wystawcy uwierzytelnienia i zaznaczył odpowiednie pole, poproś skrypty po stronie klienta o wykonanie kodu za odpowiednim przyciskiem „Zaloguj się za pomocą” przed załadowaniem , pamiętaj tylko, aby serwer przechowywał unikalny identyfikator w tabeli automatycznego logowania, która zawiera nazwę użytkownika, identyfikator sesji i wystawcę uwierzytelnienia użytego dla użytkownika. Ponieważ te metody logowania korzystają z AJAX, i tak czekasz na odpowiedź, i ta odpowiedź jest albo potwierdzoną odpowiedzią, albo odrzuceniem. Jeśli otrzymasz poprawną odpowiedź, użyj jej jak zwykle, a następnie kontynuuj ładowanie zalogowanego użytkownika jak zwykle. W przeciwnym razie logowanie się nie powiedzie, ale nie mów użytkownikowi, po prostu kontynuuj, ponieważ nie jesteś zalogowany, zauważy to. Ma to na celu zapobieżenie, aby osoba atakująca, która ukradła pliki cookie (lub sfałszowała je w celu zwiększenia uprawnień), nie dowiedziała się, że użytkownik automatycznie loguje się na stronie.
Jest to tanie i przez niektórych może być również uważane za brudne, ponieważ próbuje ono zweryfikować potencjalnie już zalogowany użytkownik w miejscach takich jak Google i Facebook, nawet o tym nie mówiąc. Nie należy go jednak stosować u użytkowników, którzy nie poprosili o automatyczne logowanie do witryny, a ta konkretna metoda służy tylko do zewnętrznego uwierzytelnienia, np. Google+ lub FB.
Ponieważ zewnętrzny serwer uwierzytelniający został użyty do poinformowania serwera za kulisami, czy użytkownik został zweryfikowany, osoba atakująca nie może uzyskać niczego poza unikalnym identyfikatorem, który sam w sobie jest bezużyteczny. Opracuję:
Bez względu na wszystko, nawet jeśli atakujący użyje nieistniejącego identyfikatora, próba nie powiedzie się przy wszystkich próbach, z wyjątkiem przypadków otrzymania poprawnej odpowiedzi.
Tę metodę można i należy stosować w połączeniu z wewnętrznym uwierzytelniaczem w przypadku osób, które logują się do witryny za pomocą zewnętrznego uwierzytelnienia.
=========
Teraz, dla twojego własnego systemu uwierzytelniania, który może automatycznie logować się użytkowników, robię to w ten sposób:
DB ma kilka tabel:
Pamiętaj, że nazwa użytkownika może mieć długość 255 znaków. Mój program serwera ogranicza nazwy użytkowników w moim systemie do 32 znaków, ale zewnętrzni uwierzytelniacze mogą mieć nazwy użytkowników w domenie @ domain.tld mogą być większe, więc po prostu obsługuję maksymalną długość adresu e-mail dla maksymalnej kompatybilności.
Należy zauważyć, że w tej tabeli nie ma pola użytkownika, ponieważ nazwa użytkownika po zalogowaniu znajduje się w danych sesji, a program nie zezwala na dane zerowe. Identyfikator_sesji i znacznik_sesji można wygenerować za pomocą losowych skrótów md5, skrótów sha1 / 128/256, znaczników datetime z losowymi ciągami dodanymi do nich, a następnie skrótu lub cokolwiek zechcesz, ale entropia danych wyjściowych powinna pozostać tak wysoka, jak to możliwe ogranicz ataki brutalnej siły nawet przed zejściem z ziemi, a wszystkie skróty generowane przez twoją klasę sesji powinny być sprawdzane pod kątem dopasowania w tabeli sesji przed próbą ich dodania.
Adresy MAC z natury mają być UNIKALNE, dlatego sensowne jest, aby każdy wpis miał unikalną wartość. Z drugiej strony nazwy hostów można legalnie powielać w oddzielnych sieciach. Ile osób używa „Home-PC” jako jednej z nazw swoich komputerów? Nazwa użytkownika jest pobierana z danych sesji przez zaplecze serwera, więc manipulowanie nią jest niemożliwe. Jeśli chodzi o token, ta sama metoda generowania tokenów sesji dla stron powinna być używana do generowania tokenów w plikach cookie dla automatycznego logowania użytkownika. Na koniec dodaje się kod daty i godziny, kiedy użytkownik będzie musiał ponownie zweryfikować swoje poświadczenia. Zaktualizuj tę datę i godzinę przy logowaniu użytkownika, zachowując ją w ciągu kilku dni, lub zmuś ją do wygaśnięcia, niezależnie od ostatniego logowania, zachowując ją tylko przez miesiąc lub więcej, w zależności od projektu.
Zapobiega to systematycznemu fałszowaniu adresu MAC i nazwy hosta dla użytkownika, który zna automatyczne logowanie. NIGDYniech użytkownik zachowa plik cookie z hasłem, czystym tekstem lub w inny sposób. Niech token zostanie zregenerowany na każdej stronie nawigacji, tak jak token sesji. To znacznie zmniejsza prawdopodobieństwo, że osoba atakująca może uzyskać prawidłowy plik cookie tokena i użyć go do zalogowania. Niektóre osoby próbują powiedzieć, że osoba atakująca może ukraść pliki cookie ofiary i przeprowadzić atak polegający na powtórzeniu sesji w celu zalogowania. Gdyby atakujący mógł ukraść pliki cookie (co jest możliwe), z pewnością naraziłby na szwank całe urządzenie, co oznacza, że mógł po prostu użyć urządzenia do zalogowania się, co jest sprzeczne z celem kradzieży plików cookie całkowicie. Tak długo, jak twoja strona działa w oparciu o HTTPS (co powinno mieć miejsce w przypadku haseł, numerów CC lub innych systemów logowania), zapewniasz użytkownikowi pełną ochronę, jaką możesz uzyskać w przeglądarce.
Należy pamiętać o jednej rzeczy: dane sesji nie powinny wygasać, jeśli użyjesz automatycznego logowania. Możesz utracić możliwość fałszywego kontynuowania sesji, ale sprawdzanie poprawności w systemie powinno wznowić dane sesji, jeśli oczekuje się, że będą to trwałe dane między sesjami. Jeśli chcesz mieć zarówno trwałe, jak i nietrwałe dane sesji, użyj innej tabeli dla trwałych danych sesji z nazwą użytkownika jako PK i poproś serwer, aby pobierał je tak, jak normalne dane sesji, po prostu użyj innej zmiennej.
Po uzyskaniu takiego logowania serwer powinien nadal sprawdzać poprawność sesji. Tutaj możesz kodować oczekiwania dotyczące skradzionych lub zainfekowanych systemów; wzorce i inne oczekiwane wyniki logowania do danych sesji często mogą prowadzić do wniosków, że doszło do przejęcia systemu lub fałszowania plików cookie w celu uzyskania dostępu. W tym miejscu ISS Tech może wprowadzić reguły, które uruchomiłyby blokadę konta lub automatyczne usunięcie użytkownika z systemu automatycznego logowania, utrzymując atakujących wystarczająco długo, aby użytkownik mógł określić, w jaki sposób atakujący odniósł sukces i jak je odciąć.
Na zakończenie należy się upewnić, że wszelkie próby odzyskania, zmiany hasła lub niepowodzenia logowania przekraczające próg powodują wyłączenie automatycznego logowania, dopóki użytkownik nie potwierdzi poprawnie i nie potwierdzi tego.
Przepraszam, jeśli ktoś spodziewał się, że w mojej odpowiedzi zostanie podany kod, to się tutaj nie wydarzy. Powiem, że używam PHP, jQuery i AJAX do uruchamiania moich stron i NIGDY nie używam Windowsa jako serwera ... kiedykolwiek.
źródło
Poleciłbym podejście wymienione przez Stefana (tj. Postępował zgodnie ze wskazówkami w Najlepszych praktykach dotyczących ulepszonego trwałego logowania plików cookie), a także zaleciłbym, aby upewnić się, że pliki cookie są plikami cookie HTTP, aby nie były one dostępne dla potencjalnie złośliwego kodu JavaScript.
źródło
Wygeneruj skrót, być może z tajemnicą, którą tylko ty znasz, a następnie zapisz go w swojej bazie danych, aby można go było powiązać z użytkownikiem. Powinien działać całkiem dobrze.
źródło
Moje rozwiązanie jest takie. Nie jest w 100% kuloodporny, ale myślę, że pozwoli ci to zaoszczędzić w większości przypadków.
Po pomyślnym zalogowaniu się utwórz ciąg z tymi informacjami:
Szyfruj
$data
, ustaw typ na HttpOnly i ustaw cookie.Gdy użytkownik wróci do Twojej witryny, wykonaj następujące czynności:
:
postacią.W przypadku wylogowania użytkownika usuń ten plik cookie. Utwórz nowy plik cookie, jeśli użytkownik ponownie się zaloguje.
źródło
Nie rozumiem koncepcji przechowywania zaszyfrowanych danych w pliku cookie, gdy jest to jego zaszyfrowana wersja, której potrzebujesz do hakowania. Jeśli czegoś brakuje, proszę o komentarz.
Myślę o takim podejściu do „Remember Me”. Jeśli widzisz jakiekolwiek problemy, prosimy o komentarz.
Utwórz tabelę do przechowywania danych „Remember Me” - osobno do tabeli użytkowników, aby móc zalogować się z wielu urządzeń.
Po udanym logowaniu (z zaznaczonym Zapamiętaj mnie):
a) Wygeneruj unikalny losowy ciąg znaków, który będzie używany jako identyfikator użytkownika na tym komputerze: bigUserID
b) Wygeneruj unikalny losowy ciąg: bigKey
c) Przechowuj ciasteczko: bigUserID: bigKey
d) W tabeli „Remember Me” dodaj rekord z: UserID, Adres IP, bigUserID, bigKey
Jeśli próbujesz uzyskać dostęp do czegoś, co wymaga logowania:
a) Sprawdź plik cookie i wyszukaj bigUserID i bigKey z pasującym adresem IP
b) Jeśli go znajdziesz, zaloguj się, ale ustaw w tabeli użytkownika „miękkie logowanie”, aby w przypadku niebezpiecznych operacji można było poprosić o pełne logowanie.
Po wylogowaniu zaznacz wszystkie rekordy „Remember Me” dla tego użytkownika jako wygasłe.
Jedyne luki, jakie widzę, to;
źródło
Przeczytałem wszystkie odpowiedzi i nadal trudno mi było wydobyć to, co miałem zrobić. Jeśli zdjęcie jest warte 1 tys. Słów, mam nadzieję, że pomoże to innym wdrożyć bezpieczne trwałe przechowywanie w oparciu o Ulepszone trwałe logowanie Barry'ego Jaspana.
Jeśli masz pytania, opinie lub sugestie, postaram się zaktualizować schemat, aby odzwierciedlić dla nowicjuszy próbującego wdrożyć bezpieczne trwałe logowanie.
źródło
Wdrożenie funkcji „Keep Me Logged In” oznacza, że musisz dokładnie zdefiniować, co to będzie oznaczać dla użytkownika. W najprostszym przypadku użyłbym tego, aby sesja miała znacznie dłuższy limit czasu: 2 dni (powiedzmy) zamiast 2 godzin. Aby to zrobić, będziesz potrzebować własnego miejsca do przechowywania sesji, prawdopodobnie w bazie danych, abyś mógł ustawić niestandardowe czasy wygaśnięcia dla danych sesji. Następnie musisz upewnić się, że ustawiłeś plik cookie, który będzie trwał przez kilka dni (lub dłużej), a nie wygasa po zamknięciu przeglądarki.
Słyszę, jak pytasz „dlaczego 2 dni? Dlaczego nie 2 tygodnie?”. Wynika to z faktu, że użycie sesji w PHP automatycznie przesunie termin ważności z powrotem. Wynika to z faktu, że wygaśnięcie sesji w PHP jest w rzeczywistości bezczynnością.
Teraz, powiedziawszy to, prawdopodobnie zaimplementuję trudniejszą wartość limitu czasu, którą przechowuję w samej sesji i po około 2 tygodniach, i dodam kod, aby to zobaczyć i na siłę unieważnić sesję. A przynajmniej je wylogować. Oznacza to, że użytkownik będzie musiał okresowo logować się. Wieśniak! robi to
źródło
Myślę, że możesz po prostu to zrobić:
Sklep
$cookiestring
w bazie danych i ustaw jako plik cookie. Ustaw także nazwę użytkownika tej osoby jako plik cookie. Chodzi o to, że skrótu nie można poddać inżynierii wstecznej.Gdy pojawi się użytkownik, uzyskaj nazwę użytkownika z jednego pliku cookie, niż
$cookieString
z innego. Jeśli$cookieString
pasuje do tego zapisanego w bazie danych, użytkownik jest uwierzytelniany. Ponieważ hasło_hash używa za każdym razem innej soli, nie ma znaczenia, co to jest czysty tekst.źródło