Kontrola długości danych
Czy podczas pobierania danych od użytkowników, które następnie przesyłane są do bazy kontrolujecie ich długość? Generalnie jeśli nie operujemy na wrażliwych danych operacje tą można pominąć. Podczas zapisu zbyt dużej porcji danych SZBD usunie jej nadmiar i ukończy operacje z powodzeniem nie generując żadnego błędu. Jednak czy na pewno kwestia kontroli długości danych jest błahą sprawą? Jak pokazuje przypadek WordPress’a nie.
Brak kontroli długości wprowadzanych danych doprowadził do możliwości zresetowania hasła dowolnego użytkownika, a co za tym idzie przejęcia jego konta. Poniżej przykładowy scenariusz ataku wykorzystującego ten błąd.
Akcja
Po pierwsze musimy znać login użytkownika, którego konto chcemy przejąc. Podczas instalacji WordPress’a tworzone jest domyślne konto administratora o loginie „admin”, co więcej zmiana owego loginu, nie jest możliwa spod panelu administratora. Jest to duży błąd ponieważ potencjalny napastnik od razu wie jakim loginem posługuje się administrator.
Kiedy posiadamy już nazwę użytkownika wystarczy tylko założyć konto o takim samym loginie i skorzystać z opcji przypominania hasła. Może się wydawać, że operacja ta jest niemożliwa do wykonania, ponieważ przed dodaniem nowego użytkownika sprawdzane jest czy podany login jest wolny. Nic bardziej mylnego.
Z zasady sprawdzanie czy podany login jest wolny odbywa się w następujący sposób:
SELECT id FROM users WHERE login = "admin";
Jeśli zapytanie zwróci jakieś rekordy oznacza, to że podany login jest już zajęty i należy wygenerować odpowiedni błąd. Co się jednak stanie jeśli jako login podamy nie „admin”, a
„admin 123”? Podany login nie zostanie znaleziony w bazie, a co za tym idzie konto zostanie pomyślnie założone.
Teraz tylko pojawia się pytanie co ma „admin” do „admin 123” w końcu są to dwa różne konta. Są różne, ale tylko tak długo jak nie bierzemy pod uwagę długości pola, w którym przechowywany jest login. Jeśli założymy, że długość pola to 8 znaków, nasz login zmieni postać z „admin 123” na „admin ”. Jak można zauważyć jedyna różnica jaka pozostała pomiędzy naszym loginem, a loginem „admin” to kilka spacji. I tu z pomocą przychodzi nam jedna z właściwości mySQL. Podczas zapisywania danych usuwa on wszystkie spacja z początku oraz końca ciągu. Dzięki temu uzyskujemy taki sam login jaki posiada administrator.
W celu lepszego zrozumienia powyższego opisu kawałek kodu, który zademonstruje problem.
$login = 'admin 123'; // sprawdzenie czy login jest wolny $query = mysql_query( ' SELECT id FROM users WHERE login = "' . $login . '";' ); $query = mysql_fetch_object( $query ); // jeśli zapytanie nie zwróciło wyniku login jest wolny // i można utworzyć konto if( !$query ) { mysql_query( ' INSERT INTO users( login ) VALUES( "' . $login . '" )' ); // login został przycięty zgodnie z długością pola oraz zostały // usunięte spacje; dzięki temu $login == 'admin' }
Wykonanie powyższego kodu skutkuje pojawieniem się w bazie drugiego konta o loginie „admin”.
Obrona
Tak samo jak w większości przypadków tak i z tym problemem można sobie łatwo poradzić. Najprostszym sposobem jest kontrola rozmiaru przesyłanych danych, tak aby zgadzał się on z rozmiarem pola w bazie.
Istnieje jeszcze drugi sposób zabezpieczenia związany z projektowaniem bazy. Mianowicie należy nałożyć na pole „login” indeks UNIQUE. Dzięki temu będziemy mieli pewność, że każdy login w bazie będzie unikalny, a próba dodania istniejącego loginu zakończy się błędem.
Tagi: przejęcie konta, sql
Kategoria: Bezpieczeństwo, Bazy danych
bardzo ciekawy artykuł, napisz może przykładowe rozwiązanie (kod) :) albo najlepiej całościowo zoptymalizuj wp i udostępnij WordPress 2.6 PL by Bełdzio :D
Generalnie to nie ma co pisać :-) Dodajesz index UNIQUE na odpowiednie pole + korzystając ze strlen( ) dla PHP sprawdzasz długość danych :-)
Bełdzio jak zawsze ciekawy wpis :) Sam padłem ofiarą tego błędu na zapomnianym WP.
A która baza danych tak robi? to nie najlepsze rozwiązanie, powinna nie pozwolić na umieszczenie rekordu…
Tomasz Bartczak
Racjonalny Developer
biorąc pod uwagę, że WordPress działa na mySQL? :)) jak się dobrze skonfiguruje baze to nie będzie pozwalała na takie rzeczy
Co do strlen, to jest szybszy sposób na sprawdzenie czy dany string nie jest za długi.
Załóżmy, że maksymalna dozwolona długość to 8, wówczas piszemy cos takiego:
if(isset($string{8})) //tu obsługa błędu
Ogólnie stringa możemy traktowac jak tablice, tylko zamiast nawiasów kwadratowych używamy klamer. Isset natomiast jest dużo szybsze od strlen.
Ot taka drobna optymalizacja :)
Jak najbardziej można tak zrobić, aczkolwiek nie jestem pewien czy w nowszych wydaniach PHP takie traktowanie stringów nie zostało / zostanie usunięte.
Na stronie manuala nic na ten temat nie piszą, więc raczej od tego nie odejdą w najbliższej przyszłości. W każdym razie php 5.4 jeszcze to obsługuje :)
btw. doczytałem, że kwadratowych nawiasów też można użyć
W takim razie moje niedopatrzenie :)