Wcześniej pisałem, że w trakcie fazy analizy wymagań dla aplikacji webowej i tworzenia jej projektu, warto zdefiniować dokładnie jakie dane będą do niej wchodziły i określić na podstawie tych informacji reguły ich walidacji. Sama walidacja to nie wszystko, bo może okazać się, że w określone pole powinno się dać wpisać wszystko... I wówczas pojawia się kolejne zagrożenie, czyli interpreter injection.
Walidacja to nie wszystko
Na czym polega interpreter injection? Dane wpisywane przez użytkownika są poddawane dalszemu przetwarzaniu, mogą być wykorzystane jako fragment zapytania SQL (SQL injection), ale również wypisane na stronę (w rezultacie może to prowadzić do Cross-Site Scripting poprzez HTML injection), zapisane w formacie XML, wykorzystane w zapytaniu LDAP... Ogólnie wszędzie tam, gdzie dane wpisywane przez użytkownika są interpretowane, może zaistnieć interpreter injection, czyli to, co wykona interpreter nie do końca jest tym, co zamierzał programista. Najczęściej stosowany przykład to formularz logowania, w którym wprowadzane są dwa parametry $USERNAME i $PASSWORD, które z kolei są wstawiane do zapytania SQL typu SELECT username FROM users WHERE users.username='$USERNAME' AND users.password='$PASSWORD'. Wykorzystanie takiego sformuowania jest banalne, wystarczy za $USERNAME podstawić dowolny ciąg znaków, za $PASSWORD natomiast coś w stylu ' OR ''=' (tak, trochę inny przykład niż powszechnie wykorzystywany z ' or 1=1 --). Co wówczas się dzieje? Zapytanie SQL zaczyna przyjmować formę SELECT username FROM users WHERE users.username='cokolwiek' AND users.password='' OR ''=''. Takie zapytanie ZAWSZE zwróci jakiś rezultat, ponieważ ''='' jest ZAWSZE prawdziwe. Jest to przykład, gdy atakujący przekazuje dane, które następnie modyfikują zapytanie wykonywane w bazie SQL.
Innym przykładem może być XSS (Cross-Site Scripting), który sam z siebie zasłużył na własny punkt w liście OWASP TOP10, a który ostatnio stał się nad wyraz popularny dzięki.... (odpuszczę sobie). Tutaj interpreterem jest przeglądarka, a wstrzykiwany jest zwykle kod JavaScript, choć czasami sam kod HTML też wystarczy, by narazić właściciela strony na utratę wizerunku.
Przykłady można mnożyć, ale jaka powinna być strategia ochrony przed atakami? Pierwsza to oczywiście walidacja danych, druga to "zamaskowanie" znaków mających specjalne znaczenie w danym zastosowaniu. Trzeba pamiętać, że zbiór znaków jest inny dla HTML, inny dla LDAP, inny dla SQL. Dlatego też za każdym razem, gdy dane pochodzące od użytkownika mają zostać wykorzystane (przekazane do interpretera), należy zamaskować znaki odpowiednie dla konkretnego użycia. Dla przykładu w specyfikacji języka XML zdefiniowane są określone znaki, które muszą zostać escapowane. Analogicznie dla HTML, minimalnie znaki <, > & oraz " powinny być zastępowane odpowiednimi encjami, odpowiednio <, >, & oraz ". W popularnych językach znajdują się już zdefiniowane funkcje wykonujące to zadanie, wystarczy wspomnieć o htmlspecialchars lub htmlentities, trzeba tylko pamiętać o ich wykorzystaniu w odpowiedniej chwili.
W przypadku SQL z kolei warto zrezygnować z "ręcznego" tworzenia zapytań i zamiast tego wykorzystać mechanizm typu Prepared Statement. Jest to mechanizm, który wcale nie ogranicza się jedynie do Java, tego typu ochronę można równie dobrze stosować w przypadku innych języków i baz danych, na przykład PHP i MySQL, a nawet Python i sqlite.
...i w ten prosty sposób łącząc ze sobą walidację danych i odpowiednie praktyki ich "oczyszczania" przed wykorzystaniem sprawiamy, że tworzona aplikacja jest bezpieczniejsza. Oczywiście to jeszcze nie 100% bezpieczeństwa, ale w rezultacie stosowania tych zaleceń otrzymuje się aplikację niejednokrotnie bezpieczniejszą, niż aplikacje dostarczane przez Wielkich Polskiego Rynku IT za Ciężkie Pieniądze.