Jak zrezygnowaliśmy z Vue.js na rzecz niezawodności: historia jednego modułu uwierzytelniania
Powtarzające się problemy z zapętlaniem logowania skłoniły nas do zbadania przyczyn. Okazało się, że starsze urządzenia i przeglądarki w połączeniu z firmowym oprogramowaniem zabezpieczającym generowały ruch przypominający atak DDoS z powodu specyfiki Vue.js. Podjęliśmy decyzję o przeniesieniu usługi uwierzytelniania na najprostszy stos: HTML, CSS i czysty JavaScript, bez bibliotek. Po migracji problem całkowicie zniknął, a logowanie działa stabilnie także na starszych urządzeniach użytkowników.
Kontekst: duży projekt i SSO
Nasz zespół został zaproszony do dużego projektu, w którym odpowiadaliśmy za moduł jednokrotnego logowania (SSO) — wspólny punkt logowania do wszystkich pozostałych usług ekosystemu.
Stos technologiczny projektu:
- Backend: Laravel
- Frontend: Vue.js
- Baza danych: MySQL
- Kolejki: RabbitMQ
- Uwierzytelnianie: SAML
Nasza rola wydawała się standardowa: zapewnić użytkownikom płynne logowanie do wszystkich usług za pośrednictwem jednego modułu. Wszystko działało — do pewnego momentu.
Problem: zapętlające się logowanie
Zaczęły regularnie napływać zgłoszenia: podczas próby logowania strony wpadały w pętlę, a użytkownicy nie mogli się zalogować.
Użytkownicy skarżyli się na:
- Niekończące się przekierowania.
- Zawieszanie się na ekranie logowania.
- Brak możliwości ukończenia procesu uwierzytelniania.
Początkowo wyglądało to na odosobniony błąd. Jednak liczba zgłoszeń rosła, ponieważ z serwisu zaczęto korzystać na masową skalę. Zaczęliśmy szczegółowo analizować problem.
Diagnostyka: co zakłócało uwierzytelnianie
Wdrożyliśmy rozszerzone logowanie zdarzeń, zebraliśmy dane z frontendu i backendu oraz przeanalizowaliśmy wzorce ruchu. Odkryliśmy coś nieoczywistego.
Część użytkowników korzystała ze starszych urządzeń i przeglądarek. Samo w sobie nie stanowiło to problemu — wiele usług obsługuje starsze środowiska. Jednak u tych użytkowników zainstalowano firmowe oprogramowanie zabezpieczające ze specyficzną konfiguracją, którego stosowanie było wymagane przez dział bezpieczeństwa firmy.
Mechanizm wyglądał następująco:
- Przeglądarka ładowała stronę logowania opartą na Vue.js.
- Firmowe oprogramowanie zabezpieczające analizowało ruch i zachowanie strony.
- Ze względu na specyfikę działania Vue.js — dynamiczne ładowanie, żądania asynchroniczne i aktualizacje DOM — oprogramowanie błędnie interpretowało tę aktywność.
- W odpowiedzi zaczynało blokować żądania lub inicjować je ponownie.
- Każde takie żądanie trafiało na serwer, a cały proces powtarzał się wielokrotnie.
W rezultacie setki użytkowników korzystających z takiego oprogramowania nieświadomie generowały ruch odpowiadający atakowi DDoS na nasz serwis uwierzytelniania — nie ze złej woli, lecz wskutek błędnej reakcji oprogramowania ochronnego.
Problemem nie było samo obciążenie. Problem polegał na konflikcie nowoczesnego frontendu JavaScript z firmowymi systemami zabezpieczeń działającymi po stronie klienta.
Rozwiązanie: minimalizm zamiast nowoczesności
Podjęliśmy niepopularną, ale w tej sytuacji jedyną słuszną decyzję: przenieść serwis uwierzytelniania na możliwie najprostszy stos technologiczny.
Wykorzystaliśmy:
- HTML — statyczną strukturę strony, bez dynamicznego generowania.
- CSS — zwykłe style, bez frameworków i preprocesorów.
- Czysty JavaScript — bez bibliotek, frameworków i narzędzi do budowania aplikacji.
Usunęliśmy:
- Vue.js wraz z całym jego ekosystemem.
- Asynchroniczne ładowanie komponentów.
- Routing SPA.
- Zbędne żądania XHR na etapie uwierzytelniania.
Kluczowa zasada: strona logowania powinna być na tyle prosta, żeby nie „przestraszyła” żadnej przeglądarki ani żadnego systemu zabezpieczeń.
Rezultat: zero incydentów
Po przejściu na prosty stos technologiczny:
- Nie odnotowaliśmy ani jednego incydentu związanego z działaniem naszego serwisu.
- Nie występowały konflikty z firmowym oprogramowaniem zabezpieczającym.
- Zniknęły zapętlenia i niekończące się przekierowania.
- Obciążenie serwera uwierzytelniania zmniejszyło się kilkukrotnie.
Funkcjonalność przy tym nie ucierpiała: użytkownicy nadal korzystają z jednokrotnego logowania do wszystkich usług ekosystemu.
Wnioski z tej historii
Nowoczesny stos nie zawsze wygrywa
Vue.js, React i inne frameworki świetnie sprawdzają się w produktach, których odbiorcą jest zwykły użytkownik z nowoczesną przeglądarką. Jednak w przypadku krytycznego modułu uwierzytelniania w środowisku korporacyjnym priorytetem nie jest szybkość tworzenia oprogramowania, lecz przewidywalność i kompatybilność.
Użytkownik nie zawsze jest winny
Gdy pojawiają się zgłoszenia dotyczące „starych urządzeń”, łatwo zrzucić winę na użytkownika. Prawdziwa przyczyna często leży jednak głębiej: na styku starszych przeglądarek i nowoczesnego frontendu powstają konflikty niewidoczne w środowisku testowym.
Logowanie zdarzeń to podstawa diagnostyki
Bez szczegółowych logów nigdy nie zauważylibyśmy, że problem nie tkwi w samym obciążeniu, lecz we wzorcu żądań. Logi pokazały, że żądania nie pochodziły od „atakujących”, ale od rzeczywistych użytkowników korzystających z oprogramowania ochronnego.
Czasem krok wstecz oznacza postęp
Rezygnacja z Vue.js na rzecz czystego JavaScriptu wyglądała jak krok wstecz. W rzeczywistości była to decyzja inżynierska podyktowana warunkami eksploatacji, a nie modą.
Na zakończenie
Ta historia nie jest o tym, że Vue.js jest zły. Pokazuje, że decyzje architektoniczne należy podejmować w oparciu o kontekst, a nie trendy.
W module uwierzytelniania dużego ekosystemu korporacyjnego minimalizm okazał się bardziej niezawodny niż nowoczesność. To właśnie taki przypadek, w którym „prościej” oznacza „lepiej”.