Kłopotliwe ciągi znaków, czyli jak, w łatwy sposób, zepsuć formularz (⌐■_■)
· maciej
Kluczowymi elementami chyba większości aplikacji biznesowych są formularze, dzięki którym użytkownicy wprowadzają do systemu dane. Przetestowałem w życiu niezliczoną ich liczbę, zarówno w aplikacjach webowych, jak i desktopowych (często można to było bardziej nazwać znęcaniem, niż testowaniem). Bardzo często efektem były różnego typu exceptiony. Miało to miejsce zwłaszcza w aplikacjach typu legacy, bo ktoś, kiedyś, szybko, nikomu nie przeszkadzało, nikt tam nie zaglądał, nikt nie testował...
Nieprawidłowa walidacja danych wejściowych wprowadzanych przez użytkownika jest powszechna. Źle wprowadzone dane trafiają później do bazy danych w swojej błędnej formie i po jakimś czasie powodują problemy m.in. z przetwarzaniem, analizą i prezentacją (np. białe znaki). Nie raz widziałem mechanizmy przetwarzające pliki, np. XML, CSV, TXT, czy JSON-y, które wysypywały się przez jeden biały znak zaszyty gdzieś w nazwie kontrahenta.
Dobre praktyki
Dobrą praktyką w tworzeniu nowoczesnych i bezpiecznych aplikacji powinna być weryfikacja wszystkich danych wejściowych aplikacji, m.in.:
- formularzy HTML
- żądań RESTowych
- parametrów URL
- nagłówków HTTP
- ciasteczek
- feedów RSS
używając walidacji opartej o białą listę znaków (oczywiście po stronie frontendu - JS/HTML5, jak i serwera). Czyli np. w naszym formularzu, mając pole kwota transakcji, umożliwiamy wprowadzanie jedynie cyfr z zakresu 1-9 i przecinka/kropki, jako separatora. Kolejną zasadą jest to, iż powinno się zawęzić ilość znaków możliwych do wprowadzenia. Jednak z tym też trzeba uważać, bo np. możemy pominąć pewne warunki brzegowe (np. najdłuższe nazwisko w Polsce ma 51 znaków). Należy zachować szczególną czujność, zwłaszcza gdy tworzy się aplikacji dla klientów zza granicy, gdyż nie wszystko jest takie, jakie zakładamy.
Sobienie Kiełczewskie Pierwsze i Przedmieście Szczebrzeszyńskie - to najdłuższe nazwy miejscowości w Polsce. Obie liczą po 30 znaków.
Czy testowana przez Ciebie aplikacja umożliwia wprowadzenie ich w pole miejscowość?
Następnym krokiem powinno być, o ile to możliwe dla danego pola, wymuszenie wzorca wprowadzanych wartości. Dla przykładu - polski kod pocztowy może mieć jedynie 6 znaków i format XX-XXX - jedynie takie wypełnione pole powinno być przekazywane do backendu. Można to wykonać m.in. za pomocą wprowadzonego w HTML5 atrybutu pattern <input type="text" pattern="^\d{2}-\d{3}$">. Ogólną zasadą powinno być projektowanie formularzy w taki sposób, aby użytkownik wprowadzający był jak najbardziej ograniczony. To sprawi, że zmniejszy się prawdopodobieństwo popełnienia przez niego błędu, a my otrzymamy poprawne dane, których nie będzie trzeba później czyścić (np. usuwać myślników z NIP-u).
Big list of naughty strings
Załóżmy, że testowana przez nas aplikacja nie posiada walidacji opartej o białą listę znaków i możemy wprowadzać wartości, jakie tylko dusza zapragnie. W takim razie - jak zabrać się do psucia testowania formularzy? Ja wykorzystuję znane wielu testerom repozytorium big-list-of-naughty-strings. Lista zawiera naprawdę wiele typów stringów, którymi możemy użyć nie tylko w aplikacjach webowych, ale i desktopowych, czy terminalowych. Są także specjalne znaki, które powodowały problemy na specyficznych platformach, jak np. iOS. Ja w swojej pracy korzystam przede wszystkim z kategorii Numeric Strings, oraz Script Injection. Kiedyś, korzystając z wolnej chwili, wykorzystałem listę i stworzyłem stronę, z której korzystam w swojej pracy - Kłopotliwe stringi, która nieco ułatwia wybór interesujących nas ciągów znaków.
Ciekawostką, którą znalazłem na liście znaków w repozytorium jest ciąg %x%x%x%x, który ustawiony jako nazwa urządzenia łączącego się przez Bluetooth, umożliwiał zawieszenie oprogramowania w odtwarzaczu CD w... BMW 330i 2011. Jak widać, możliwości testowania są niemal nieograniczone.
Ṱ̺̺̕o͞ ̷i̲̬͇̪͙n̝̗͕v̟̜̘̦͟o̶̙̰̠kè͚̮̺̪̹̱̤ ̖t̝͕̳̣̻̪͞h̼͓̲̦̳̘̲e͇̣̰̦̬͎ ̢̼̻̱̘h͚͎͙̜̣̲ͅi̦̲̣̰̤v̻͍e̺̭̳̪̰-m̢iͅn̖̺̞̲̯̰d̵̼̟͙̩̼̘̳ ̞̥̱̳̭r̛̗̘e͙p͠r̼̞̻̭̗e̺̠̣͟s̘͇̳͍̝͉e͉̥̯̞̲͚̬͜ǹ̬͎͎̟̖͇̤t͍̬̤͓̼̭͘ͅi̪̱n͠g̴͉ ͏͉ͅc̬̟h͡a̫̻̯͘o̫̟̖͍̙̝͉s̗̦̲.̨̹͈̣ ̡͓̞ͅI̗̘̦͝n͇͇͙v̮̫ok̲̫̙͈i̖͙̭̹̠̞n̡̻̮̣̺g̲͈͙̭͙̬͎ ̰t͔̦h̞̲e̢̤ ͍̬̲͖f̴̘͕̣è͖ẹ̥̩l͖͔͚i͓͚̦͠n͖͍̗͓̳̮g͍ ̨o͚̪͡f̘̣̬ ̖̘͖̟͙̮c҉͔̫͖͓͇͖ͅh̵̤̣͚͔á̗̼͕ͅo̼̣̥s̱͈̺̖̦̻͢.̛̖̞̠̫̰ ̗̺͖̹̯͓Ṯ̤͍̥͇͈h̲́e͏͓̼̗̙̼̣͔ ͇̜̱̠͓͍ͅN͕͠e̗̱z̘̝̜̺͙p̤̺̹͍̯͚e̠̻̠͜r̨̤͍̺̖͔̖̖d̠̟̭̬̝͟i̦͖̩͓͔̤a̠̗̬͉̙n͚͜ ̻̞̰͚ͅh̵͉i̳̞v̢͇ḙ͎͟-҉̭̩̼͔m̤̭̫i͕͇̝̦n̗͙ḍ̟ ̯̲͕͞ǫ̟̯̰̲͙̻̝f ̪̰̰̗̖̭̘͘c̦͍̲̞͍̩̙ḥ͚a̮͎̟̙͜ơ̩̹͎s̤.̝̝ ҉Z̡̖̜͖̰̣͉̜a͖̰͙̬͡l̲̫̳͍̩g̡̟̼̱͚̞̬ͅo̗͜.̟ ̦H̬̤̗̤͝e͜ ̜̥̝̻͍̟́w̕h̖̯͓o̝͙̖͎̱̮ ҉̺̙̞̟͈W̷̼̭a̺̪͍į͈͕̭͙̯̜t̶̼̮s̘͙͖̕ ̠̫̠B̻͍͙͉̳ͅe̵h̵̬͇̫͙i̹͓̳̳̮͎̫̕n͟d̴̪̜̖ ̰͉̩͇͙̲͞ͅT͖̼͓̪͢h͏͓̮̻e̬̝̟ͅ ̤̹̝W͙̞̝͔͇͝ͅa͏͓͔̹̼̣l̴͔̰̤̟͔ḽ̫.͕ Z̮̞̠͙͔ͅḀ̗̞͈̻̗Ḷ͙͎̯̹̞͓G̻O̭̗̮
Białe znaki, gdzie jesteście?
Wiele plików, czy parsowanych tekstów może zawierać niedrukowane znaki, które czasem psują różnego rodzaju importery, a nie są widoczne gołym okiem. Jak w prosty i szybki sposób znaleźć je w pliku? Ja używam do tego Notepada++. Jest to jedno z moich ulubionych narzędzi, które było domyślnie zainstalowane chyba w każdym miejscu, w którym do tej pory pracowałem.
Jak więc wyświetlić te ukryte znaki? Wystarczy w menu wybrać Widok -> Niewidoczne znaki -> Pokaż wszystkie znaki, bądź wybrać na pasku narzędzi znak paragrafu ¶:
Powoduje to wyświetlenie w linii tekstu, na czarnym tle, kodu danego znaku. W tym przypadku jest to [CR] LF . Są to niedrukowane znaki - carriage return i line feed, które oznaczają koniec linii w systemie Windows (systemy Unix używają do tego tylko LF).
Moje odkrycia
Jednym z moich najciekawszych odkryć w karierze testera była podatność SQL Injection w jednej z aplikacji sprzedażowych. Wystarczyło wprowadzić w jedno z pól apostrof, zamknąć średnikiem i wprowadzić dowolne zapytanie '; DROP TABLE dbo.Faktury (pisane z pamięci). Oczywiście użyłem DROP TABLE (⌐■_■) i usunąłem testową tabelę z sukcesem. Nikt widocznie nie przewidział takiej sytuacji (+ uroki legacy code). Przy okazji wyszedł tutaj problem zbyt wysokich uprawnień użytkownika bazodanowego używanego przez aplikację - powinny one być jak najmniejsze.
Mam nadzieję, że powyższy wpis wpłynie lepiej na jakość testowanego, czy tworzonego przez was oprogramowania.
Źródła
[1] OWASP - Application Security Verification Standard 4.0, Rozdział 5.
[2] CVE - 2017-9212
Dodaj nowy komentarz
Brak komentarzy