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). Nieraz 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ń REST-owych
- parametrów URL
- nagłówków HTTP
- ciasteczek
- feedów RSS
używając walidacji opartej na białej liście 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ć liczbę 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 57 znaków). Należy zachować szczególną czujność, zwłaszcza gdy tworzy się aplikacje 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 na białej liście 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órych 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, ustawiony jako nazwa urządzenia łączącego się przez Bluetooth, który 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 pozytywnie wpłynie 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


