AplikacjeCyberbezpieczeństwoPolecane tematy
Privacy by design, czyli jak ograniczać użycie danych, gdy integracje wymagają coraz więcej
Integracja z platformą społecznościową zaczyna się niewinnie: aplikacja potrzebuje tokenu, treści posta i pliku. Szybko dochodzą jednak identyfikatory kont, warianty treści, statusy publikacji, logi, ponowienia i kopie robocze. Privacy by design nie polega wówczas na dopisaniu polityki prywatności do gotowego systemu. Zaczyna się od trudniejszego pytania: których danych w ogóle nie zapisywać, a które usunąć możliwie szybko po osiągnięciu celu?

RODO wymaga, aby dane osobowe gromadzone oraz przetwarzane w zastosowaniach biznesowych były adekwatne, stosowne i ograniczone do konkretnego celu. Te same reguły dotyczą także aplikacji biznesowych i obowiązują twórców oprogramowania. Dodatkowo, towarzyszą im m.in. ograniczenia przechowywania danych w fazie projektowania rozwiązań i domyślnej ochrony w myśl idei privacy by design.
W praktyce, istotna jest tu również specyfika poszczególnych aplikacji oraz stojącego za nimi stosu technologicznego. W rozwiązaniu SaaS korzystającym z zewnętrznych API to dostawca platformy aplikacyjnej dostawca częściowo narzuca zakres oraz format danych, ale aplikacja – czy raczej jej twórca – nadal decyduje, co zostanie zachowane i na jak długo. Minimalizacja zakresu i właściwa retencja to jednak nie jedyne wymagania mające na celu zapewnienie zgodności z RODO – osobnej analizy wymagają role stron, podstawy prawne, obowiązki informacyjne i transfery poza EOG.
Podczas projektowania systemu publikującego treści przez kilka zewnętrznych API przekonałem się, że najważniejszym narzędziem privacy by design nie jest pojedyncza biblioteka szyfrująca. Jest nim model danych, który od początku rozdziela sekrety, treść użytkownika, media i telemetrię oraz nadaje każdej z tych kategorii własny cel i czas życia. W niniejszym artykule postaram się szerzej omówić te doświadczenia. W dużej mierze są one bowiem dość uniwersalne i dotyczą nie tylko konkretnych aplikacji, ale też całych środowisk aplikacyjnych, w szczególności tych, które wykorzystują usługi chmurowe.
Integracja może wymagać danych, których aplikacja nie musi zachowywać
Odpowiedź API bywa znacznie bogatsza niż informacja potrzebna do wykonania konkretnej funkcji. Platforma może zwrócić nazwę konta, identyfikatory zasobów, zestaw uprawnień, parametry profilu i dodatkowe metadane. Najprościej zapisać cały obiekt „na przyszłość”. To wygodne w pierwszej wersji produktu, lecz tworzy magazyn danych bez jasno określonego celu, co z kolei może naruszać reguły wynikające z przepisów prawa.
Jednocześnie, wymóg minimalizacji danych nie oznacza ograniczania liczby pól za wszelką cenę ani automatycznego odrzucania każdego szerszego zakresu OAuth. Jeżeli dostawca udostępnia publikację tylko w ramach określonego uprawnienia, może być ono technicznie niezbędne. Nie znaczy to jednak, że aplikacja musi korzystać ze wszystkich danych dostępnych w ramach tego uprawnienia. W praktyce, projektując takie rozwiązanie, należy zatem odpowiedzieć na trzy różne pytania:
- Jakich danych i uprawnień wymaga platforma, aby wykonać operację?
- Które elementy odpowiedzi są potrzebne aplikacji w trakcie tej operacji?
- Które z nich muszą pozostać po jej zakończeniu – i jak długo?
To rozróżnienie jest istotne. „Dostawca zwrócił” nie znaczy „musimy przechować”. Podobnie „użytkownik udzielił dostępu” nie znaczy „każda część systemu może z niego korzystać”. Aktualne dobre praktyki OAuth zalecają ograniczanie uprawnień i redukowanie skutków przejęcia tokenu. W architekturze przekłada się to na kontrolę miejsca jego użycia, obsługę wygaśnięcia i unieważnienia, rotację tokenu odświeżania tam, gdzie wspiera ją dostawca oraz blokowanie przypadkowego zapisu jawnej wartości.
Najpierw mapa danych, dopiero później tabele
Przed zaprojektowaniem schematu bazy warto również precyzyjnie rozpisać mapę przepływu danych w aplikacji. Dla każdej kategorii danych powinna ona zawierać co najmniej: cel, źródło, miejsce przetwarzania, odbiorców oraz osoby i komponenty uprawnione do dostępu, okres użyteczności, regułę usunięcia oraz zachowanie w razie błędu.
W systemie publikującym przez zewnętrzne API powstaje zwykle pięć odmiennych kategorii danych:
- materiał uwierzytelniający – token dostępu (access token), token odświeżania (refresh token) i informacje o ich ważności;
- dane kierujące operacją – wybrane konto, strona, profil, lokalizacja lub inny cel publikacji;
- treść użytkownika – tekst, warianty dla poszczególnych kanałów i ustawienia publikacji;
- media – przesłane zdjęcia lub filmy, pliki tymczasowe i adresy umożliwiające ich pobranie;
- telemetria i ślad operacyjny – etap procesu, wynik, kod błędu, czas trwania, liczba prób i identyfikator korelacyjny.
Nie powinny one trafiać do jednego uniwersalnego „rekordu integracji”, ponieważ mają inne poziomy ryzyka i inne okresy przydatności. Token może być potrzebny przez czas aktywnego połączenia, plik tylko do wykonania publikacji, a kod wyniku jeszcze przez pewien czas do obsługi reklamacji lub diagnostyki. Połączenie wszystkiego upraszcza zapytania, ale rozszerza skutki błędu: jeden zrzut bazy, niekontrolowany log albo nadmierne uprawnienie ujawnia wtedy cały kontekst użytkownika.
W jednym z projektów rozdzieliłem dane połączenia z platformą, treść publikacji, rejestr obiektów multimedialnych i rejestr prób. Rekord próby nie potrzebuje treści posta. Do odpowiedzi na pytanie „na którym etapie operacja się nie powiodła?” wystarczą nazwa platformy, status, kod błędu, numer próby, czas i informacja, czy błąd kwalifikuje się do ponowienia. Jest to przykład minimalizacji, która jednocześnie poprawia czytelność systemu. Mniejszy zakres danych oznacza też mniejszy skutek incydentu, prostszy audyt i niższy koszt utrzymania diagnostyki.
Rozdzielenie logiczne ma znaczenie nawet we wspólnej bazie
Koncepcja privacy by design nie wymaga, aby każda kategoria danych trafiała do osobnego systemu lub osobnej instancji bazy danych. Granice powinny być jednak rzeczywiste i możliwe do wykazania, a więc widoczne w modelu, kodzie, uprawnieniach oraz – tam, gdzie jest to potrzebne – w sposobie szyfrowania.
Co nie mniej istotne, tokenów nie należy traktować jak zwykłych pól tekstowych. W opisywanym przeze mnie rozwiązaniu token dostępu i token odświeżania są szyfrowane tą samą technologią, ale z innym kontekstem kryptograficznym. Dzięki temu wartości przypisanej do określonego typu tokenu i platformy nie da się poprawnie odszyfrować w innym kontekście. Format przechowuje także identyfikator klucza, co umożliwia rotację bez jednoczesnego unieważniania wszystkich połączeń.
To nadal separacja logiczna i kryptograficzna, a nie deklaracja, że tokeny znajdują się w fizycznie odrębnym skarbcu. Taką granicę warto opisać audytorowi bez przypisywania architekturze cech, których nie ma.
Podobna zasada dotyczy dostępu. Komponent przygotowujący podgląd posta nie potrzebuje tokenu odświeżania. Moduł raportowy nie musi odczytywać mediów. Mechanizm publikacji powinien otrzymywać tylko te dane, które są konieczne w danym kroku i dla konkretnego użytkownika. Zasada najmniejszych uprawnień staje się wtedy właściwością przepływu, a nie wyłącznie konfiguracją konta w chmurze.
Retencja powinna zmieniać się wraz ze stanem operacji
Model privacy by design dotyka też m.in. retencji danych. Jedna globalna wartość, na przykład „przechowujemy wszystko przez 30 dni”, rzadko jest dobrą polityką. Plik przed zaplanowaną publikacją, historia wyniku i token aktywnego połączenia mają odmienne cele oraz okresy przydatności.
Moje doświadczenia pokazują, że dobrze sprawdza się tu model stanowy. W przypadku natychmiastowej publikacji treści poprzez API konkretnej platformy prywatny obiekt multimedialny otrzymuje krótki, z góry określony czas przechowywania. Aplikacja udostępnia go zewnętrznej platformie przez podpisany adres ważny tylko przez krótki czas, a reguła cyklu życia automatycznie kwalifikuje obiekt do usunięcia po upływie tego terminu. W razie błędu przygotowania pliku lub anulowania aplikacja może również spróbować usunąć plik od razu.
Publikacja zaplanowana wymaga innej decyzji. Pliku nie można usunąć przed terminem wykonania. Kopia przeznaczona dla harmonogramu otrzymuje czas przechowywania powiązany z planowaną datą i krótkim zapasem na obsługę ponowień. Przy anulowaniu aplikacja próbuje usunąć ją od razu, natomiast reguła cyklu życia pozostaje niezależnym mechanizmem awaryjnego usunięcia. RODO nie wyznacza tu konkretnej liczby godzin. Termin wynika z celu, zachowania dostawcy, mechanizmu ponowień i oceny ryzyka – powinien być jawny, uzasadniony, automatycznie wykonywany i monitorowany.
Retencję trzeba rozpatrywać także poza główną bazą: w kolejkach, kopiach zapasowych, plikach tymczasowych, pamięci podręcznej, rejestrach błędów i narzędziach wsparcia. Usunięcie kolumny z tabeli nie rozwiązuje problemu, jeżeli pełne dane żądania przetrwały w kolejce nieobsłużonych zadań albo w systemie obserwowalności.
Potrzebne są też jawne wyjątki. Jeżeli udokumentowana podstawa prawna wymaga zachowania określonych danych — na przykład do ustalenia, dochodzenia lub obrony roszczeń — lepszy jest audytowalny mechanizm wstrzymania usuwania (legal hold) niż ukryte „nie usuwamy, bo może się przydać”. Wyjątek powinien mieć właściciela, podstawę, zakres i warunek zakończenia.
Logi nie mogą stać się drugą bazą treści
Największa pokusa nadmiarowego przetwarzania pojawia się podczas awarii. Gdy zewnętrzne API zwraca niejasny błąd, naturalną reakcją jest zapisanie całego żądania i odpowiedzi. Takie podejście pomaga odtworzyć pojedynczy przypadek, ale może skopiować do logów token, treść posta, adres pliku, identyfikator konta i dane zwrócone przez dostawcę. System diagnostyczny staje się wtedy słabiej chronioną repliką danych produkcyjnych.
Organizacja OWASP (Open Worldwide Application Security Project) zaleca, aby tokenów dostępowych, haseł, kluczy i wrażliwych danych osobowych nie zapisywać bezpośrednio w logach. Zależnie od celu należy je usuwać, maskować lub chronić kryptograficznie. Sama lista zakazanych nazw pól nie wystarcza. Sekret może znaleźć się w tekście wyjątku, nagłówku autoryzacyjnym, zagnieżdżonej odpowiedzi albo podpisanym adresie URL.
Warto zatem zastosować podejście warstwowe. Podstawą jest lista dozwolonych pól: mechanizm logujący przyjmuje tylko znane klucze operacyjne. Dodatkowe reguły maskują lub odrzucają podejrzane pola i wartości, a osobna warstwa usuwa z błędów publikacji nagłówki, dane żądania, treści i adresy mediów. Ograniczana jest także długość tekstu, głębokość obiektu i wielkość kolekcji, aby rozbudowany obiekt błędu nie został zapisany w całości. Maskowanie i usuwanie wrażliwych pól są dodatkowymi warstwami ochrony, a nie gwarancją rozpoznania każdego sekretu.
Przydatny log może nadal zawierać nazwę etapu, platformę, kod, wynik, czas trwania, możliwość ponowienia i techniczny identyfikator korelacyjny. Taki identyfikator nie jest automatycznie anonimowy. Jeżeli stabilna wartość pozwala powiązać wiele zdarzeń dotyczących tej samej osoby, nadal trzeba objąć ją kontrolą dostępu i retencją. Poza tym, identyfikator wyniku dostawcy lub odnośnik do opublikowanego zasobu może być potrzebny w śladzie audytowym. To świadomy kompromis: każde takie pole powinno mieć uzasadnienie, ograniczony dostęp i termin usunięcia.
Ponowienia i częściowy sukces są testem architektury
Jednoczesna publikacja treści (danych) do kilku miejsc (platform, aplikacji, środowisk) nie zawsze kończy się jednym wynikiem. Jeden kanał może przyjąć treść, drugi odpowiedzieć błędem przejściowym, a trzeci odrzucić ustawienie wymagające decyzji użytkownika. Naiwne ponowienie całego żądania grozi duplikacją już opublikowanego materiału. Z kolei zapisanie pełnych danych żądania „na wszelki wypadek” przedłuża retencję treści. Potrzebne staje się zaimplementowanie bardziej rozbudowanej logiki.
Lepszy model śledzi wynik osobno dla każdego wybranego celu publikacji. Ponowienie obejmuje wyłącznie nieudaną część, korzysta z minimalnego zapisu stanu potrzebnego do odtworzenia operacji i ma własny limit czasu oraz liczbę prób. Klucz idempotencji – unikalny identyfikator zapobiegający ponownemu wykonaniu tej samej operacji – powinien identyfikować operację, a nie zawierać tekst użytkownika. Kolejka, historia prób i interfejs wsparcia – jeżeli zawierają dane osobowe – podlegają tym samym regułom minimalizacji co główna aplikacja.
Tu prywatność spotyka się z niezawodnością. Zbyt wąski zakres zgromadzonych informacji utrudnia ponowienie operacji, zbyt szeroki – tworzy trwałą kopię danych. Potrzebny jest zatem minimalny, wersjonowany zestaw danych niezbędnych do ponowienia operacji oraz wyraźne oddzielenie stanu biznesowego od telemetrii.
Człowiek przed publikacją to kontrola ryzyka, nie slogan prawny
Uwierzytelnienie użytkownika, autoryzacja dostępu do konta i zatwierdzenie konkretnej publikacji to trzy różne procesy kontrolne. Aktywna sesja odpowiada na pytanie „kto korzysta z systemu”. Sprawdzenie własności połączenia i mediów odpowiada na pytanie „czy wolno mu użyć tych zasobów”. Dopiero ekran podsumowania odpowiada na pytanie „czy zamierza wysłać tę treść do tych miejsc w tym terminie”.
W projektowanym przeze mnie przepływie publikacja, czy też wysyłka danych, nie rozpoczyna się bezpośrednio po wypełnieniu formularza. System najpierw waliduje dane, a następnie pokazuje użytkownikowi wybrane konta, miejsca docelowe, termin i wariant treści dla każdego kanału. Dopiero świadome ich potwierdzenie uruchamia operację.
Nie jest to uniwersalny wymóg RODO ani dowód zgody na każde przetwarzanie. To decyzja produktowa, która ogranicza ryzyko pomyłki i niezamierzonej publikacji. Ma szczególne znaczenie tam, gdzie jedna akcja może opublikować różne warianty treści w kilku zewnętrznych usługach, a cofnięcie operacji nie jest w pełni kontrolowane.
Kontrola człowieka nie zwalnia jednak serwera z własnych obowiązków. Warstwa serwerowa nadal musi sprawdzić sesję, uprawnienia, własność zasobów, dozwolone cele i wymagania danej platformy. Zaufanie wyłącznie do listy przesłanej przez przeglądarkę byłoby jedynie pozorem kontroli.
Kontrola, której nie sprawdzamy, pozostaje deklaracją
Rozliczalność wymaga czegoś więcej niż opisu architektury. To być może trywialna, ale części pomijana obserwacja. W opisywanym modelu trzeba wykazać, że jawne tokeny nie są zapisywane, mechanizm oczyszczający nie przepuszcza do logów zdefiniowanych klas sekretów – także gdy pojawiają się w zagnieżdżonych błędach, użytkownik nie odczyta cudzego obiektu, a mechanizm retencyjny usunie dane lub ograniczy dostęp po ustaniu celu — z uwzględnieniem udokumentowanych wyjątków.
Takie własności warto zamieniać w testy i kontrole operacyjne. Test pomoże sprawdzić, czy syntetyczny token nie pojawił się w logu. Zadanie retencyjne powinno raportować, ile rekordów zakwalifikowano do usunięcia, ile pozostawiono na podstawie jawnego wyjątku i ile zachowano w ograniczonej formie zgodnie z udokumentowaną procedurą. Monitoring powinien też wykrywać pliki starsze niż dopuszczalny czas przechowywania — sam harmonogram nie dowodzi, że usuwanie działa. Te same założenia powinny być fundamentem wszystkich zmian w architekturze aplikacji czy środowisk IT.
Równocześnie, każdy nowy kanał, wariant treści lub komunikat błędu może ominąć dotychczasową mapę danych. Dlatego przegląd privacy by design powinien być częścią kryteriów odbioru każdej integracji czy szerszej zmiany w oprogramowaniu.
Privacy by design to architektura świadomych braków
Najbardziej dojrzałe systemy nie wyróżniają się tym, ile danych potrafią zgromadzić, lecz tym, jak precyzyjnie potrafią uzasadnić ich obecność. Przed uruchomieniem integracji warto zatem postawić sobie następujące pytania:
- Jaki konkretny cel ma każde zapisywane pole?
- Czy pełna odpowiedź dostawcy jest potrzebna, czy wystarczy znormalizowany podzbiór?
- Czy tokeny, treść, media i telemetria mają rozdzielone granice dostępu?
- Co usuwa dane po udanej publikacji, błędzie, anulowaniu i wygaśnięciu konta?
- Jaki mechanizm zapasowy usunie dane, gdy podstawowy proces zawiedzie?
- Czy błąd da się zdiagnozować bez kopiowania danych żądania i sekretów?
- Czy użytkownik przed nieodwracalną operacją widzi jej rzeczywisty zakres?
Poza tym, trzeba pamiętać, że istotą podejścia privacy by design nie jest jednorazowe działanie czy etap procesu projektowania aplikacji lub całego stosu współzależnych rozwiązań. Mapa danych, retencja i zasady logowania powinny być przeglądane wraz z każdym nowym API, kanałem i mechanizmem diagnostycznym.
W praktyce najbezpieczniejsza dana to często ta, której system nigdy nie zapisał. Druga w kolejności to ta, którą potrafił automatycznie usunąć, gdy przestała być potrzebna.
Jarosław Maladyn, programista Java i twórca platformy Spreenity
Źródła
- Rozporządzenie Parlamentu Europejskiego i Rady (UE) 2016/679, w szczególności art. 5, 17 ust. 3 lit. e, 25 i 32
- Europejska Rada Ochrony Danych, Wytyczne 4/2019 dotyczące artykułu 25 — uwzględnianie ochrony danych w fazie projektowania oraz domyślna ochrona danych
- OWASP, Logging Cheat Sheet
- IETF, RFC 9700: Best Current Practice for OAuth 2.0 Security
- OWASP, Authorization Cheat Sheet







