Jedyna różnica pojawia się przy znaku /, który w drugim przypadku zyskuje postać \/. Czy ma to jakieś znaczenie w tym konkretnym kontekście? Może w pewnym, niewielkim stopniu ma, ale przed XSS nie chroni, przykład:
“><foo bar=”
Różnica w stosunku do poprzedniego przykładu sprowadza się do tego, że w tym przypadku nie jestem w stanie uzyskać tagu:
ponieważ funkcja escapowania znaków spowoduje jego przekształcenie do postaci:
<\/script>
Użycie String.fromCharCode natomiast obchodzi drobną niedogodność związaną z brakiem znaków ', “ oraz / dostępnych w “czystej” postaci. Jak widać całkiem dobrze można sobie poradzić bez nich.
Raz, dwa, trzy (to teraz). To make long story short:
?@[\]^_`{|}~')“>demo 3
Co jest tu nie tak? Cóż, przykładowy payload, czyli:
'+alert(/xss/)+'
w tym przypadku nie zadziała, będzie rezultat w kodzie strony będzie wyglądał tak:
demo 3
Jak widać znak ' jest wypisywany jako \', co jest właściwym sposobem escapingu tego znaku. Podobnie zresztą jak \\\\” jest właściwym sposobem escapingu znaku “, uwaga, w JavaScript. Problem tylko w tym, że nie jesteśmy (wyłącznie) w kontekście JavaScript, ale również w kontekście atrybutu HTML. A skoro tak, to:
“><foo bar=”
da w rezultacie:
demo 3
Zagmatwane? Przeglądarka zobaczy to tak (IE i Firefox):
Poprzednio okazało się, że encoding HTML w kontekście atrybutu HTML, który zawiera JavaScript, nie jest zbyt skuteczny. Tak samo encoding właściwy dla JavaScript stosowany w przypadku JavaScript, który zawarty jest w atrybucie HTML, nie jest wystarczający. Zupełnie inaczej sytuacja wyglądałaby, gdyby złożyć ze sobą te dwa sposoby kodowania znaków. Najpierw dane wstawiane do kontekstu JavaScript zabezpieczyć w sposób właściwy dla JavaScript właśnie, a potem, skoro trafiają do atrybutu HTML, całość zakodować w sposób właściwy dla tego kontekstu. Wyglądałoby to mniej więcej tak:
demo 3
Jak zobaczy to przeglądarka – do sprawdzenia we własnym zakresie :)
Jak łatwo zauważyć, jedyną różnicą w stosunku do poprzedniego przypadku jest dodatkowe kodowanie znaku “. Dobrze to widać, jeśli oba przypadki zestawi się ze sobą:
demo 1
demo 2
Można się domyślić, że problemu z XSS w tym kontekście ta zmiana raczej nie rozwiązuje.
Pierwsze wyjaśnienie odnośnie ćwiczeń z nieprawidłowego encodingu. Na pierwszy ogień pójdzie pierwsze miejsce, w którym wprowadzone dane są wypisywane. Jako string testowy wykorzystam następujący zestaw znaków:
!“#$%25&'()*+,–./:;<=>?@[]^_`{|}~
Na wyjściu natomiast dostaje się coś takiego:
demo 1
Jak widać, specjalnie traktowane są tylko nieliczne znaki, które są przekształcane w następujący sposób:
& –> &
< –> <
> –> >
Byłoby to zabezpieczenie wystarczające, gdyby dane te były wypisywane w kontekście HTML. Problem w tym, że nie są. Są wypisywane w kontekście atrybutu HTML, który, tak się dodatkowo składa, zawiera kod JavaScript.
Pod adresem http://bootcamp.threats.pl/lesson09a/ znajduje się prosty przykład “aplikacji”, która nie do końca poprawnie stosuje encoding danych wyjściowych. Przekazane dane są wypisywane w kilku miejscach, w różnym kontekście z wykorzystaniem różnego encodingu. Każdy z zastosowanych sposobów encodingu danych pozwala na XSS (na różne sposoby), choć nie w każdym(?) kontekście.
To tak w nawiązaniu do tego, że właściwy encoding jest trudny. W przeciwieństwie do encodingu, ten przykład trudny specjalnie nie jest. Miłej zabawy!
P.S. W zasadzie można się zastanawiać, czy i w którym przypadku stosowany jest encoding, a w którym escaping.
Gmail zaczął witać swoich użytkowników w następujący sposób:
O tym, że warto mieć unikalne hasła w różnych serwisach mówi się zwykle wówczas, gdy pojawia się jakaś informacja o możliwym wycieku danych użytkowników (w szczególności haseł lub stosunkowo łatwych do złamania hashy) z jakiegoś bardziej lub mniej popularnego serwisu. Zastanawiam się jakie są efekty takich dyskusji i jaki będzie efekt informacji wyświetlanej w Gmailu.
Ja tylko po raz kolejny podpowiem, że do przechowywania haseł i do generowania unikalnych haseł do różnych serwisów można wykorzystać na przykład KeePass. O tym, jak z niego korzystam pisałem tutaj: Jak korzystam z KeePass.
Drodzy programiści! Zapamiętajcie sobie dwie proste rzeczy:
wszystkie dane pochodzące z niezaufanego źródła (np. od użytkownika) przed użyciem trzeba zakodować w sposób właściwy dla kontekstu użycia,
właściwy encoding jest trudny,
W związku z tym bardzo proszę, nie róbcie tego sami, bo zrobicie to najprawdopodobniej źle. Zamiast tego wykorzystajcie proszę dostępne w bibliotekach (np. ESAPI, Microsoft Web Protection Library) funkcje napisane przez ludzi, którzy w temacie siedzą nieco głębiej. Przecież w wielu innych przypadkach nie wynajdujecie koła na nowo, tylko korzystacie z tego, co ktoś już napisał i się sprawdziło. Dlaczego z bezpieczeństwem ma być inaczej?
Jeśli już musicie robić to sami, to proszę, popatrzcie jak zrobił to ktoś, kto zrobił to dobrze. Choćby tutaj. Nawet jeśli okaże się, że w takich implementacjach są błędy, to prawdopodobnie będzie ich mniej, niż w tych “własnej roboty”, a i szansa na szybkie usunięcie jest zdecydowanie większa.
Oczywiście jest jeszcze walidacja danych wejściowych, ale to jest nieco inny temat.
Dziś wpis na temat różnicy między zagrożeniem a ryzykiem. Może najpierw na przykładach, przez wyliczenie. Przykładowe zagrożenia, bez specjalnego tematu przewodniego:
W cytowanym wpisie postawiłem następujące pytanie:
(...) w jakim przypadku opisany sposób uzyskania potwierdzenia przelewu, który w rzeczywistości nie został wykonany, jednak ma (większy) sens?
Odpowiedź pojawiła się w komentarzach do wpisu. A w oderwaniu od szczegółów można ją sprowadzić do stwierdzenia, że pozyskanie potwierdzenia przelewu z banku przed wykonaniem tego przelewu ma sens wówczas, gdy atakujący nie może go (dokładnie) sfałszować. W komentarzach pojawił się wątek podpisu cyfrowego i jest to właśnie ten przykład, który miałem na myśli.
Dziś w ramach ciekawostki zapomniana sztuka pisania \*.bat. Ale najpierw kontekst. A kontekstem jest to pytanie:
(...) Pamiętam konkretne cyfry, liczby, symbole i wyrazy ale po kilku latach nie pamiętam kolejności a kombinacji przy haśle ok 15-25 znakowym jest za dużo by klepać na piechotę. Czy ktoś może mi poradzić jak stworzyć słownik do “jakiegoś programu” który w miarę szybko by pokombinował... (...)