Kolejny odcinek serii o tym czego NIE należy robić w trakcie tworzenia aplikacji internetowych. Tym razem o tym, dlaczego nie należy wstawiać zmiennych przekazywanych przez użytkownika bezpośrednio do skryptów JavaScript.
Wstawianie zmiennych bezpośrednio do skryptu jest ZŁE!
Ufanie użytkownikowi jest ZŁE!
Pierwsza prawda - każda zmienna (nie tylko parametr), która przekracza granicę bezpieczeństwa musi być traktowana jako niezaufana i potencjalnie niebezpieczna. Oznacza to, że all user input must be validated, ze szczególnym naciskiem na all.
Często popełnianym błędem jest walidowanie jedynie tych pól, w które użytkownik może bezpośrednio wpisać dane z poziomu GUI. Mowa oczywiście o walidacji po stronie serwera, bo walidację po stronie użytkownika realizowaną przy pomocy JavaScript należy uznać za nieistniejącą. W każdym razie często nie są walidowane parametry, które w GUI występują pod postacią:
- checkboxów,
- radiowych pól wyboru,
- list wielokrotnego wyboru,
- list dropdown,
- pól hidden (te akurat w GUI nie występują),
Zakładanie, że użytkownik wartości parametrów związanych z takimi polami nie zmodyfikuje (bo ich nie widzi) jest słuszne jedynie, jeśli założymy, że wszyscy użytkownicy mają dobre intencje. Takiego założenia czynić nie można. Skutkiem braku walidacji może być w szczególności:
- SQL Injection,
- XSS,
- braki w kontroli dostępu,
Dalej zajmę się specyficzną sytuacją, w której wartość parametru przekazanego przez użytkownika jest wstawiana bezpośrednio do skryptu JavaScript na stronie zwracanej użytkownikowi, co prowadzi do (zwykle) Non-Persistent XSS.
Encoding encodingowi nierówny
Drugim elementem obrony przed XSS (choć właściwie przed każdym typem interpreter injection) jest encoding danych. Problem w tym, że encoding ten musi być context sensitive. Oznaca to po prostu, że należy go prowadzić w sposób odpowiadający sposobowi (miejscu), w którym mają być użyte. Dla przykładu jeśli mowa o polu typu hidden, gdzie wartość wstawiana jest w atrybucie value (na zasadzie value="wartość") wystarczające jest encoding znaku " do postaci ". Jeśli ta sama wartość ma być wypisana bezpośrednio w treści strony (na przykład w tagu p) encoding znaku " będzie już niewystarczający. W szczególności inne zasady encodingu będą obowiązywały, gdy interpreterem jest coś, co obsługuje HTML, a inne, gdy dany fragment jest interpretowany jako JavaScript (że o SQL już tutaj nie wspomnę).
Gdzie leży problem
Stosunkowo często zdarza się tak, że:
- dane wejściowe inne niż pola typu input nie są walidowane,
- zawartość tych pól jest wpisywana bezpośrednio w kod funkcji JavaScript,
- nie jest realizowany encoding na wyjściu lub jest on nieodpowiedni,
Jak łatwo się domyśleć skutkiem zaistnienia tych warunków jest XSS.
Osobiście wolałbym wstawiać parametry, które muszą być użyte w jakiejś funkcji JavaScript do pól typu hidden i odczytywał je z poziomu JavaScript. Dlaczego tak? Bo widzę, że o ile programiści potrafią w sposób spójny realizować encoding w polach hidden (albo nie programiści, tylko framework, z którego korzystają), to nader często nie radzą sobie z encodingiem właściwym dla JavaScript. Tak więc w ramach proaktywnego podejścia do bezpieczeństwa jestem za zlikwidowaniem problemu u samego źródła, czyli zaprzestania wypisywania danych przekazywanych przez (niezaufanego, czyli KAŻDEGO) użytkownika bezpośrednio w kodzie JavaScript.