Ostatnio szukałem informacji na temat FormAuthentication w ASP.NET. Znalazłem między innymi taki przykład...
Tak rodzą się błędy
Wzorzec projektowy copy & paste jest jednym z najczęściej stosowanych. Można przypuszczać, że zamieszczone na tej stronie przykłady kodów zostały powielone i gdzieś egzystują w działających aplikacjach. I w ten prosty sposób powielone zostały co najmniej dwa błędy.
Kontrolka logowaniaNa początek kontrolka logowania (plik LoginWebControl.ascx). Temat jest prosty, dwa pola, w które należy wpisać nazwę użytkownika i hasło. Co tu można zepsuć? Teoretycznie nic. W praktyce jednak się da. Walidacji poddawane jest, czy pola z nazwą użytkownika i hasłem są wypełnione. Dodatkowo sprawdzane jest, czy hasło ma co najmniej 5 znaków z zakresu A-Z,a-z i 0-9. Czyli z tej kontrolki może przyjść nazwa użytkownika zawierająca dowolne znaki oraz hasło, które zawiera co najmniej 5 znaków z określonego zakresu.
Zalogujmy się do systemu...Mechanizm uwierzytelnienia zawarty jest w pliku login.aspx. Na dzień dobry wita nas zmienna strSQL tworzona metodą sklejania stringów, czyli dokładnie tak, jak nie należy składać zapytań SQL. Zapytanie zwraca parę userId i userPassword. Ma ono postać następującą:
"SELECT userId, userPassword FROM UserLoginInfo WHERE userName='" + UserLoginInfo.UserId + "'"
Piękne sql-injection. Do czego wykorzystać je w tym przypadku? Proponowałbym w polu z nazwą użytkownika (brak walidacji się kłania) wpisać coś w stylu:
niemamnie' UNION SELECT 'admin', 'haslo
A w polu z hasłem wpisać po prostu haslo. Rezultatem zapytania powinna być para admin i haslo, no chyba, że istnieje użytkownik niemamnie lub akurat ta baza danych nie wspiera UNION SELECT.
Idziemy dalej. Jak sprawdzane jest, czy użytkownik podał właściwe hasło? Porównywane jest hasło wpisane przez użytkownika z hasłem odczytanym z bazy danych. Problem w tym, że dzięki sql-injection kontroluję to, co zostanie zwrócone przez bazę danych, jak również (co jest oczywiste), kontroluję hasło wpisane przez użytkownika. Ups... Fajny przykład, nie ma co. Choć z sql-injection spotykam się dość często, to już dawno nie spotkałem się z tak pięknym okazem tego błędu przy uwierzytelnianiu użytkownika. Przecież o otwierającym wiele drzwi magicznym haśle ' or 1=1 -- słyszał chyba prawie każdy.
I użytkowników ujawnia...System nie powinien "cieknąć" informacją, czy dany użytkownik istnieje, czy też nie. A jak jest w przykładzie? Jeśli nie istnieje użytkownik, system powie bardzo kulturalnie, że nie znalazł takiego użytkownika. W przypadku błędnego hasła system też bardzo kulturalnie powie, że hasło jest nieprawidłowe. Ups po raz kolejny... Dziwne jest to, że błąd ten nader często zdarza się "w naturze". Wiem, że skutki nie są jakieś spektakularne, ale jest to błąd.