Błąd typu stored HTML injection w serwisie ogłoszeń o pracę RamenHire pozwalał każdemu na wypełnienie publicznego formularza i przekształcenie powiadomień e-mail zespołu w platformę phishingową.
Jak błąd się wkradł
RamenHire działa w oparciu o Next.js i Supabase, umożliwiając startupom publikowanie ofert pracy bez konieczności logowania. Każde zgłoszenie wyzwala wysłanie wiadomości e-mail do wewnętrznego zespołu rekrutacyjnego. Treść e-maila jest tworzona poprzez bezpośrednie łączenie surowych pól formularza — nazwy firmy, opisu stanowiska, imienia i nazwiska osoby kontaktowej — w ciągu znaków HTML. Przed dotarciem ciągu do usługi pocztowej nie odbywa się żadne ucieczkowanie (escaping) ani sanityzacja danych.
Ponieważ szablon traktuje dane wejściowe użytkownika jako bezpieczny kod HTML, złośliwy kandydat może wstrzyknąć fałszywy link „kliknij tutaj, aby zweryfikować”. Payload jest zapisywany na serwerze jako część ogłoszenia o pracę, więc każdy kolejny e-mail z powiadomieniem zawiera złośliwy kod. Atakujący może ukryć ten link obok prawdziwego przycisku „View Dashboard”, co może skłonić członka zespołu do ujawnienia danych uwierzytelniających lub odwiedzenia strony phishingowej.
Dlaczego to było istotne
Wiadomości e-mail trafiają do kluczowego zespołu rekrutacyjnego — osób, które nieustannie klikają w linki, aby przesuwać kandydatów przez kolejne etapy procesu rekrutacji.
Luka techniczna
Ucieczkowanie HTML nie jest jednowymiarowym procesem. Należy chronić dwa konteksty:
- Węzły tekstowe (Text nodes) – treść między znacznikami. Zamiana
<na<oraz>na>blokuje znaczniki, ale nie powstrzymuje atakującego przed wyjściem poza wartość atrybutu. - Wartości atrybutów – ciągi znaków wewnątrz znaczników, np.
href="…". Wstrzyknięcie cudzysłowu ("lub') pozwala atakującemu na wcześniejsze zamknięcie atrybutu i wstawienie własnego kodu markup.
Oryginalny kod obsługiwał jedynie czysty tekst, pozostawiając wstrzykiwanie atrybutów całkowicie otwarte na ataki.
Rozwiązanie problemu
Deweloper dodał małą funkcję pomocniczą escapeHtml i zastosował ją do każdej wartości dostarczonej przez użytkownika przed interpolacją, niezależnie od tego, czy wartość ta trafi do węzła tekstowego, czy do atrybutu.
Kroki weryfikacyjne
- Testy lokalne – Do funkcji szablonu wprowadzono zestaw ciągów znaków służących do wstrzykiwania kodu. Każdy test wykazał jedynie ucieczkowane znaki, bez żadnych wykonywalnych znaczników.
- Testy produkcyjne – Po wdrożeniu poprawki, przez działającą stronę przesłano payload, przechodząc jednocześnie weryfikację botów Cloudflare Turnstile. Otrzymany e-mail trafił do skrzynki odbiorczej dewelopera; złośliwy link oraz wszelkie znaczniki skryptów pojawiły się jako niezrozumiały tekst, a nie klikalne elementy.
Uwaga dotycząca Gmaila: usługa ta może nadal kolorować adresy URL na niebiesko i czynić je klikalnymi, ale jest to jedynie wygoda po stronie klienta i nie oznacza, że wstrzyknięcie HTML zakończyło się sukcesem. Poprawka uniemożliwia wstrzyknięciu tworzenie prawdziwych przycisków lub uruchamianie skryptów.
Na co powinni uważać deweloperzy
- Nigdy nie ufaj danym z publicznych formularzy – Nawet nieszkodliwie wyglądające formularze mogą generować dane przechowywane (stored), które są wykorzystywane w innych miejscach.
- Stosuj ucieczkowanie w punkcie użycia – Stosuj ucieczkowanie zależne od kontekstu
