file opened

Z dziennika architekta: IdP, OTP, odpowiedzialność i profile

2026-05-08 ::5 min read

W tym przypadku podszedłem do tematu wdrożenia mechanizmu 2FA w swoim systemie. Nie należy to do trudnych zadań, problemem jest legacy, które leży jak zawsze gdzieś w systemie. O ile mój system w tym momencie nie robi posiada dużo kodu, w teorii przepisanie wymaganych elementów powinno być szybkie i łatwe. Tutaj spotykam się jednak z wewnętrznie nałożonym limitem - nie posiadam wystarczająco dużo czasu, by móc przepisywać dosłownie co tylko chcę. Oznacza to, że musiałem podjąć wybór - coś co będzie bliższe mechanizmowi 2FA, ale niekoniecznie zgodne ze wszystkimi najlepszymi praktykami lub całkowicie poddać temat i nie zrobić nic. Oczywiście, że wybrałem rozwiązanie pośrednie.

Zasada Zero Trust oraz odpowiedzialność wewnątrz systemu

Jedną z podstawowych zasad cyberbezpieczeństwa jest Zero Trust. Oznacza to w skrócie, że nie ufamy dosłownie nikomu poza jednym podmiotem, który nazywamy Identity Provider (dalej: IdP). Takowym podmiotem może być usługa wewnątrz naszego systemu, ale też dostawca taki jak Google, Microsoft czy LinkedIn. Powoduje to też przeniesienie odpowiedzialności do jednego miejsca, a więc i kontrola pozostaje w jednym, konkretnym miejscu przez co monitorowanie przepływu, a także kontrola dostępu w pełnym zakresie w całym systemie jest prostsza. Dzięki takiej konfiguracji wykrywanie potencjalnych zagrożeń lub naruszeń polityki bezpieczeństwa w systemie jest również łatwiejsze. Swoją drogą, zasada pojedynczej odpowiedzialności i wyprowadzenia pełnej odpowiedzialności do konkretnego komponentu w systemie jest generalną zasadą w architekturze.

2FA, OTP i odpowiedzialność

Zazwyczaj, w przypadku autoryzacji dwuetapowej lub wieloetapowej, generowanie kodów OTP odbywa się wewnątrz wspomnianego dostawcy tożsamości. Jest to oczywisty porządek rzeczy w kontekście systemu, którego mechanizmy autoryzacji są oparte o IdP lub ogólnie wzorzec Federated Identity. Samo generowanie kodów to kwestia na całkowicie odrębny tekst, ale można przyjąć, że podstawowe implementacje to TOTP i HOTP. Oczywiście, co do zasady OTP - One Time Password - może być po prostu losowo wygenerowaną x znakową wartością liczbową, ale i po prostu tekstową. Nie jest to najbezpieczniejsza opcja, ale nie jest też niczym złym. Wspomniane TOTP i HOTP są bezpieczne z założenia, gdyż oba polegają na generowaniu kodu na czymś co jest w jakiś sposób unikalne. TOTP - Time-based OTP - polega na wartości, która jest generowana na bazie unikalnego kodu przypisanego zwykle do danego konta, a następnie kod generuje się w oparciu o ziarno, które jest tworzone przy użyciu tego klucza i ramki czasu. HOTP - Hmac-based OTP - bazuje na wygenerowanym skrócie (hashu), a także na sekretnym kluczu, który też jest powiązany z konkretnym kontem użytkownika. Te mechanizmy są ustandaryzowane w dokumentach RFC, a każdy popularny język programowania ma implementację każdego z nich. Nie mówiąc już o dużych dostawcach tożsamości. Niekoniecznie jednak implementacje samych serwerów IdP jak Keycloak mają wymaganą elastyczność i pozwalają na implementację flow, które będzie dopasowane do starszej, niezgodnej ze standardami implementacji autoryzacji. Ten przypadek - szczególnie ten z niezgodną ze standardami implementacją - jest oczywiście mój.

Architektura oraz podjęte decyzje

Aktualna architektura opiera się na Password Flow, czyli Direct Grant Access zgodnie z OAuth2. W żadnym razie nie jest to zalecane rozwiązanie dla większości platform dzisiaj, a nawet systemów wewnętrznych, bo częściowa odpowiedzialność za uzyskanie autoryzacji i zarządzanie autoryzacją odbywa się po stronie innego systemu niż samo IdP. W moim wypadku jest to PikaCore. O ile nie polecam tego rozwiązania, stąd jednym z istotniejszych zadań dla całego systemu jest zmigrowanie na bezpieczniejszy Authorization Code Flow i przeniesienie polityk bezpieczeństwa w całości do Keycloaka, tak by PikaCore nie był już gateway’em systemowym i nie odpowiadał za analizę dostępu do wskazanego zasobu poza tym, co jest bezpośrednio w gestii dostępu biznesowego np. to, że dany użytkownik z racji na rolę może wykonać jakąś akcję, a inny pomimo dostępu do zasobu nie może. Fakt faktem jest, że taki mechanizm jest użyty, a że jak wspomniałem mam ograniczony zasób jakim jest czas, wolę mieć 2FA, które jakkolwiek współgra z tym co jest niż próbować od ręki przepisać całość na raz. Najpierw jedno, potem drugie. Stąd podjąłem decyzję, że muszę zaimplementować mechanizmy wewnątrz konsoli cloudu, które pozwolą tworzyć profil usera, aby bezpiecznie przechowywać secrety (lub po prostu pobierać je np. z keyvaulta) do generowania OTP i inne podstawowe parametry dot. kont użytkowników w odrębnym systemie. Wynika to z tego, że nie chcę dokładać do obszaru domenowego PikaCore, ale jednocześnie nie mam zapotrzebowania na to, by pewne personalne informacje znajdywały się w IdP. Także nie przechowuję tych informacji na tyle (teraz to w sumie w ogóle nie przechowuję), aby “zaprzęgać” do pracy coś pokroju LDAP czy inny agregator to typu. Jednocześnie może powstać potrzeba, by przechowywać coś, co nie jest typową informacją kontaktową jak telefon czy email. Stąd moja decyzja, by wykorzystać do tego odrębną usługę, którą już mam. Ją również czeka hardening, ale wpierw - implementacja wsparcia dla profili użytkowników. W ramach profilu będzie między innymi przechowywany secret do generowania kodów OTP, ale również tymczasowe informacje o sesji użytkownika, konfiguracja ustawień w innych modułach czy chociażby sama flaga - 2FA włączone bądź nie. Jak zawsze, docelowa implementacja będzie stworzona w myślą o bezpieczeństwie - komunikacja z systemem szyfrowana (TLS1.3), dane przechowywane szyfrowane na dysku, każde z użyciem klucza wygenerowanego na bazie frazy typowej dla usera, by zapewnić brak transparentności danych nawet dla administratora systemu.

Stan prac: WIP

Aktualnie pracę są WIP i to głównie przez… brak czasu. Chodzi głównie o to, że nie jestem w stanie pracować na razie nad tym, bo mam utrudniony dostęp do wspomnianego hosta, który serwuje konsolę systemową. Draft architektury po stronie PikaCore i front-endu są już gotowe, więc zostaje wdrożenie szkicu po stronie serwera konsoli, dodanie automatyzacji (by uniknąć takiego przestoju w przyszłości), a także wdrożyć aktualizację PikaCore, aby aktywować OTP pierwszym użytkownikom (w tym przede wszystkim sobie). Także aktualizacja postępu - w następnym wpisie z tej serii (niekoniecznie kolejnym następnym).