Mam skrypt co do którego mam zastrzeżenia jeśli chodzi o bezpieczeństwo. Nie wrzucę go ze względu na to, że jest zbyt długi. Wrzucę natomiast jego uproszczoną wersję by odnieść się do problemu. A wygląda on tak
<?php $session_data = ""; $pdo = new PDO('mysql:host=localhost; port=3306; dbname=test', 'root', ''); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->exec("SET NAMES 'utf8'"); { { $password = hash_hmac('sha256', $_POST['password'], '2NYbH5qS8J'); $stmt = $pdo->prepare("SELECT * FROM users WHERE login = :login && password = :password"); if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { } else { exit; } } else { exit; } } $max_idle_time = 900; { if ($now - $_SESSION[$session_data]['time'] < $max_idle_time) { $_SESSION[$session_data]['time'] = $now; // tu następuje pobranie z bazy danych danych przeznaczonych dla użytkownika o danym id a następnie ich wyświetlenie } else { } } else { echo '<h1>Panel Użytkownika</h1> <form method="post" action=""> <div> <input type="text" name="login" placeholder="Twój login" value=""> </div> <div> <input type="password" name="password" placeholder="Twoje hasło" value=""> </div> <button type="submit">Zaloguj się</button> </form>'; } ?>
Skrypt działa dobrze. Przy pierwszym wejściu na stronę przechodzimy na koniec kodu do formularza, ponieważ zmienna login, password i session nie istnieją.
Jeżeli wyślemy login, hasło i skrypt sprawdzi, że nie są puste to jest porównywany hash z tym znajdującym się w bazie. Dalej jeśli się zgadzają jest tworzona zmienna sesyjna, w której jest id, czas i nazwa użytkownika. W drugiej części skrypt sprawdza, że istnieje sesja między innymi z id użytkownika i wyświetla dane przeznaczone tylko dla niego.
Problem w tym, że jeżeli nie znam loginu ani hasła użytkownika to też mogę się dostać do danych przeznaczonych tylko dla danego użytkownika. Wystarczy, że w sposób "sztuczny" utworzę zmienną sesyjną z id i zmieniając tylko id mogę sobie skakać z konta na konto.
Pytanie jak zabezpieczyć taki skrypt?
PS: Trafiłem przypadkiem na ten błąd robiąc kopię tego skryptu, ale z podpiętą inną bazą innym loginem i hasłem. Odpaliłem jeden skrypt podałem login i hasło, a potem nie zamykając przeglądarki odpaliłem drugi skrypt z całkiem inną bazą loginem i hasłem w niej (tylko id użytkownika się zgadzało) i byłem zalogowany na obu skryptach. Mogłem sobie zmieniać id użytkownika w jednym skrypcie, a w drugim byłem zalogowany na tym użytkowniku o danym id. Wnioskuję więc, że każdy mógłby wysłać zmienną sesyjną z id i by został wpuszczony i by mu się wyświetliły dane tego użytkownika.
PS2: Po linii 39 następuje sprawdzenie czy została utworzona zmienna sesyjna między innymi z id użytkownika, a w linii 46 jest select który pobiera dane przeznaczone dla użytkownika o tym id. I właśnie tu jest luka, którą nie wiem jak załatać, jedyne co mi przychodzi do głowy to ponowne sprawdzenie czy nazwa użytkownika i hasło się zgadzają, ale wtedy jaki sens ma kod przed linią 39? Mam nadzieję, że jasno to opisałem

