Do sieci wyciekają dane użytkowników najpopularniejszych portali. To jest jak śnieg w styczniu – ani to zaskoczenie (tu drogowcy się mogą nie zgodzić), ani do uniknięcia. Złamać jest zawsze łatwiej niż zbudować. Dlatego dobry programista powinien być paranoikiem.
Darujmy sobie myślenie mnie to nigdy nie spotka. Nie myślmy, co się stanie jeżeli dane wyciekną, tylko jakie będą skutki, gdy, dane wyciekną.
Wyobraźmy sobie sytuację najprostszą – w bazie danych mamy, oczywiście oprócz masy innych pól z bzdurami pokroju numer telefonu, czy karty kredytowej, nazwę użytkownika i hasło. Poprawność danych przy logowaniu sprawdzamy sobie na przykład w ten sposób:
[php]
$res = $mysql_query(„SELECT * FROM uzytkownicy WHERE login = '{$_POST[’login’]}’ AND haslo = '{$_POST[’haslo’]}'”);
if(!$res) {
$zalogowany = false;
} else {
$zalogowany = true;
(…)
[/php]
W momencie, gdy napastnik przechwyci w jakiś sposób naszą bazę danych, będzie niezmiernie szczęśliwy. Oto otrzymał również hasła naszych użytkowników. A że większość z nich używa jednego hasła do wszystkiego… Wystarczy wejść na konto e-mail i je przechwycić zmieniając hasło na swoje. Później można już się kontaktować z bankiem.
Dlatego podstawowa zasada bezpieczeństwa skryptów brzmi: nigdy nie przechowuj haseł w bazie w postaci jawnej
Co więc należy zrobić. Istnieje coś takiego jak funkcje haszujące. Zamieniają one dowolny łańcuch znaków w inny, o stałej długości, zwany z j. angielskiego hash (tych, którzy po słowie „hash” obiecywali sobie coś innego serdecznie pozdrawiam, sztuczki mistrza Kelby’ego działają). Dla nas istotne są dwie cechy hashu
- hash utworzony z łańcucha znaków kilka razy zawsze będzie dokładnie ten sam, powtarzalny
- na podstawie hasha, niemożliwym jest odtworzenie pierwotnego łańcucha znaków.
Stąd lekko modyfikujemy nasz poprzedni kod i otrzymujemy:
[php]
$haslo = sha1($_POST[’haslo’]);
$res = $mysql_query(„SELECT * FROM uzytkownicy WHERE login = '{$_POST[’login’]}’ AND haslo = '{$haslo}'”);
if(!$res) {
$zalogowany = false;
} else {
$zalogowany = true;
(…)
[/php]
„sha1()” jest właśnie jedną z takich funkcji hashujących. W tym momencie, gdy napastnik dostanie się do naszej bazy danych, ostro się zawiedzie. Nie uzyska w niej wprost żadnego hasła. Nie jest to rozwiązanie w pełni bezpieczne, ale o tym następnym razem. Dziś skupmy się na zabezpieczeniu naszego kodu do końca. Albowiem sprytny napastnik w pole login wpisze nam: [cc inline=1]admin’ –[/cc] i większość naszych zabezpieczeń bierze w łeb. Bo nasz skrypt zada zapytanie do bazy danych [cc lang=”SQL”]SELECT * FROM uzytkownicy WHERE login = 'admin’ –’ AND haslo = 'wlamalemsie'[/cc]
Czym to się skończy? Bezproblemowym logowaniem na konto admina, znaki „–” powodują uznanie reszty linijki za komentarz(!). Tu więc budujemy drugą zasadę bezpiecznego obchodzenia się z hasłami – nigdy nie podajemy w zapytaniach do bazy niewalidowanych zmiennych. Często zapobiega temu dyrektywa magic_quotes w php.ini, ale nie warto na niej polegać. Dobrym pomysłem jest wyłączenie jej na początku skryptu, a później stosowanie funkcji [cc inline=1]addslashes()[/cc], aby uzyskać w ten sposób łatwo przenośny kod. Atak typu powyżej przedstawionego nosi nazwę sql injection i jest jedną z prostszych technik. Niestety, z uwagi na kiepsko napisany kod, do dzisiaj skutecznie stosowaną. Tak więc po raz trzeci tworzymy nasz kod logowania:
[php]
$login = addslashes($_POST[’login’]);
$haslo = sha1($_POST[’haslo’]);
$res = $mysql_query(„SELECT * FROM uzytkownicy WHERE login = '{$login}’ AND haslo = '{$haslo}'”);
if(!$res) {
$zalogowany = false;
} else {
$zalogowany = true;
(…)
[/php]
W przypadku hasła nie musimy stosować addslashes, ponieważ funkcja sha1 sama się o to zatroszczy. Optymistycznie kończąc na dzisiaj – mamy kod, który w kategorii bezpieczeństwa nie jest może szczególnie wyszukany, ale daje pewne podstawy.
W następnym odcinku napiszę o dalszej ochronie haseł – sam hash to za mało, gdy zależy nam na bezpieczeństwie danych użytkowników.
Leave a Reply