Apka EU do weryfikacji wieku nie została zhakowana.
15 kwietnia 2026 roku Ursula von der Leyen ogłosiła gotowość techniczną unijnego systemu weryfikacji wieku. Dzień później Paul Moore, konsultant bezpieczeństwa, opublikował na X wideo zatytułowane „Hacking the EU Age Verification app in under 2 minutes". W 48 godzin historia obiegła Reddita (ponad 3000 upvote'ów na r/privacy), Twittera, dziesiątki portali technologicznych.
Nagłówki krzyczały: ZHAKOWANO!
Jestem przeciwny weryfikacji wieku online. Uważam, że to ślepa uliczka prowadząca do masowej inwigilacji. Ale kłamstwa w słusznej sprawie to dalej kłamstwa — i ta historia to doskonały przykład tego, jak brak weryfikacji źródeł osłabia pozycję wszystkich, którzy naprawdę walczą o prywatność.
Co tak naprawdę zrobił Paul Moore?
Wziął demo aplikacji — reference implementation dostępną na GitHubie, gdzie w README czarno na białym stoi:
"The main purpose of the reference implementation is to showcase the ecosystem and act as a technical example."
Oraz:
"This is not production software."
Następnie zrootował telefon z Androidem. Odblokował ekran. Nadał uprawnienia roota file managerowi (widać to na jego własnym wideo). Otworzył katalog shared_prefs i ręcznie wyedytował plik konfiguracyjny XML — usunął zaszyfrowane wartości PIN-u, wyzerował licznik prób logowania, zmienił flagę biometrii z true na false.
I ogłosił światu: HACKED!
Dlaczego to nie jest hack
Zacznijmy od podstaw. Katalog shared_prefs na Androidzie jest sandboxowany per-app. Żadna inna aplikacja nie ma do niego dostępu — chyba że telefon jest zrootowany. A zrootowany telefon to urządzenie, na którym każda aplikacja jest skompromitowana: banking, WhatsApp, Signal, menedżer haseł. Wszystko.
Co więcej — vault z danymi tożsamości jest szyfrowany kluczami sprzętowymi w Android Keystore. Nawet po resecie PIN-u, jedyne co uzyskujesz to możliwość generowania tokenów „jestem pełnoletni" — dokładnie to samo, co widzi każdy legalny weryfikator. Dane osobowe (imię, data urodzenia) pozostają zaszyfrowane kluczem, którego nie da się wyeksportować nawet z rootem.
Czy PIN przechowywany w shared_prefs to idealny design? Nie. Ale na niezrootowanym urządzeniu jest chroniony sandboxem systemu operacyjnego i szyfrowany kluczem z TEE (Trusted Execution Environment). Hashowanie 6-cyfrowego PIN-u jest bezcelowe — brute-force w sekundy. Encryption z kluczem sprzętowym to sensowny wybór.
A Chrome extension, który „bypasował" weryfikację?
W drugim tweecie Moore pochwalił się, że zbudował rozszerzenie do Chrome'a, które „omija weryfikację wieku". Co zrobił w praktyce? Zarejestrował się legalnie w systemie (ze swoim prawdziwym dokumentem tożsamości), wyciągnął klucze kryptograficzne i wsadził je do rozszerzenia.
To jest credential sharing — tak samo jak pożyczenie komuś swojego dowodu osobistego lub PIN-u do karty. Nie bypass, nie exploit, nie podatność.
Kiedy jeden z komentujących na Twitterze zwrócił na to uwagę (@CactusKipic: „You just automated the verification using valid auth. That's not a bypass"), Moore przesunął słupki: „...and once the keys are hard-coded and shared in an extension, you bypass the auth."
Tak. I jak udostępnisz komuś swój login i hasło, to też „bypasuje" uwierzytelnianie. To nie jest odkrycie.
Gdzie zawiodła weryfikacja
Historia zebrała tysiące upvote'ów na Reddicie, setki retweetów, dziesiątki artykułów. Nikt nie sprawdził dwóch rzeczy: że to demo app z disclaimerem na GitHubie i że „atak" wymaga roota.
Wystarczyło 30 sekund na GitHubie, żeby zobaczyć disclaimer. Wystarczyło uważnie obejrzeć wideo Moore'a, żeby zauważyć root access nadany file managerowi. Nikt tego nie zrobił — bo nagłówek „ZHAKOWANO W 2 MINUTY" generuje kliki, a „badacz edytował config na zrootowanym demo" nie.
A teraz to, o czym powinniśmy rozmawiać
Ironią tej historii jest to, że EUDI Wallet naprawdę ma fundamentalne problemy. Tylko że nie te, o których pisał Moore. Są dużo poważniejsze — i dużo nudniejsze dla nagłówków.
Problem 1: Linkability tokenów (SD-JWT nie wspiera unlinkability)
Za każdym razem gdy potwierdzasz wiek, apka wysyła ten sam unikatowy podpis cyfrowy. Strona porno, bank, forum dyskusyjne — wszystkie widzą ten sam identyfikator. Mogą powiązać Twoje konta między sobą, nawet bez znajomości Twojego imienia.
A rząd, który wydał token? Ten zna i token, i tożsamość — bo sam go podpisał.
To nie spekulacja. W Issue #193 na GitHubie Architecture Reference Framework (ARF) kryptografowie piszą wprost:
„mDL and SD-JWT can not structurally support unlinkability, nor everlasting privacy. We recommend relying on anonymous credentials protocols, particularly of the BBS family."
W Discussion #211 („Cryptographers' Feedback on the EU Digital Identity's ARF") grupa kryptografów stwierdza, że „some of the currently suggested design aspects" nie spełniają wymagań prywatności eIDAS 2.0.
ARF w sekcji 4.2.3 deklaruje intencję zapobiegania śledzeniu użytkowników. Ale konkretne mechanizmy unlinkability są odłożone do przyszłych specyfikacji technicznych. Sam dokument zawiera klauzulę: „This document holds no legal value and does not prejudge the final mandatory legal requirements."
Problem 2: Rozwiązanie istnieje — ale EU go nie wybrało
I tu historia robi się naprawdę ciekawa.
W czerwcu 2024 roku zespół Applied Crypto Group z Orange Innovation opublikował techniczny raport opisujący protokół BBS# — wariant BBS+, zaprojektowany specjalnie jako rozwiązanie problemów prywatności w EUDI Wallet. W marcu 2025 opublikowali zaktualizowaną wersję z pełnymi dowodami bezpieczeństwa i benchmarkami.
Tabela porównawcza z tego raportu (Tabela 4) mówi wszystko:
| Protokół | Unlinkability (między weryfikatorami) | Unlinkability (weryfikator + wydawca) |
|---|---|---|
| BBS# | Unconditional (Everlasting Privacy) | Unconditional (Everlasting Privacy) |
| SD-JWT/mDL | No | No |
„Unconditional" oznacza, że unlinkability jest gwarantowane nawet wobec przeciwnika z nieograniczoną mocą obliczeniową. To nie jest teoretyczne ćwiczenie — Orange zaimplementował BBS# na istniejących smartfonach. Transakcja trwa ~70ms na Android StrongBox SE.
Protokół BBS# zachowuje selective disclosure (ujawniasz tylko „jestem pełnoletni", nie datę urodzenia) ale dodaje prawdziwe Zero-Knowledge Proof — matematycznie niemoźliwe do powiązania między sesjami. SD-JWT tego nie oferuje — ujawnia atrybuty wraz z podpisem wydawcy, który jest identyczny za każdym razem.
Kiedy zapytano autorów BBS#, dlaczego Komisja nie wybrała tego podejścia, odpowiedzieli:
„For some reason we totally ignore, the commission insisted from the beginning on mDL and SD-JWT. Already from the first version of the ARF when the ARF was almost empty, that was set in stone. We didn't think it was a good thing at the time and still don't think it is, especially since this choice wasn't made in a transparent way. At all."
Issue #193 zostało zamknięte 16 lipca 2025 lakonicznym komentarzem: „Closing this topic, with many thanks for all contributions."
Problem 3: Device attestation — Google i Apple jako strażnicy europejskiej tożsamości
Żeby apka EUDI Wallet w ogóle działała, Twój telefon musi „udowodnić" Google'owi (Play Integrity) lub Apple'owi (App Attestation), że nie został zmodyfikowany. Zrootowałeś Androida? Zainstalowałeś custom ROM? Używasz telefonu z GNU/Linux?
Przepraszamy, nie masz prawa potwierdzić swojego wieku.
Na desktopie nie ma klienta — wyświetla się QR code do zeskanowania telefonem. Więc nawet jeśli Francja czy Niemcy przechodzą na open source w administracji — dla obywatela nie ma to żadnego znaczenia. Żeby potwierdzić tożsamość, musisz wyciągnąć smartfona certyfikowanego przez jedną z dwóch prywatnych amerykańskich korporacji.
Dwie firmy spoza UE jako gatekeeperzy dostępu do europejskiego internetu. Ironicznie, to jest dokładnie ten rodzaj zależności, przed którym ostrzega Digital Markets Act.
Problem 4: Jeśli to nie jest „ochrona dzieci" — to co?
Warto zadać sobie pytanie, czy weryfikacja wieku to rzeczywiście ochrona nieletnich — czy może próba walki z botami i państwową dezinformacją, ubrana w narrację o dzieciach, bo otwarcie przyznanie się do tego celu byłoby geopolitycznie niewygodne.
Nie mam na to dowodu. Ale wiem jedno: system, który wymaga od każdego użytkownika udowodnienia tożsamości za każdym razem gdy chce coś opublikować w internecie, jest strukturalnie identyczny z systemem masowej inwigilacji — niezależnie od deklarowanych intencji. A jeden z komentatorów w Issue #193 nazywa obecną architekturę ARF wprost: „literally Surveillance by Design".
Podsumowanie: co powinieneś zapamiętać
Paul Moore nie zhakował apki EU do weryfikacji wieku. Wyedytował plik konfiguracyjny na zrootowanym demo telefonie i zrobił z tego viralowy content. A prawdziwe problemy — architektura nadzoru, brak unlinkability, monopol platform — przeszły niezauważone.
Bo są nudne. Bo wymagają czytania specyfikacji kryptograficznych zamiast oglądania dwuminutowych filmików.
Chcesz walczyć z weryfikacją wieku? Walcz argumentami:
- Pytaj o linkability tokenów — dlaczego SD-JWT zamiast BBS#?
- Pytaj o device attestation — dlaczego Google i Apple kontrolują dostęp do europejskiej tożsamości?
- Pytaj o transparentność — dlaczego Komisja od pierwszej wersji ARF narzuciła SD-JWT „in a way that was not transparent at all"?
- Czytaj Issue #193 i Discussion #211 na GitHubie ARF — tam są głosy kryptografów, których Komisja zignorowała.
Nie podawaj dalej clickbaitu. To osłabia pozycję nas wszystkich.
Źródła:
- Paul Moore — oryginalne wideo „Hacking the EU Age Verification app" (X/Twitter)
- EUDI Wallet Android Reference Implementation (GitHub) — z disclaimerem „not production software"
- Issue #193: „Privacy has not started to be taken into account" — dyskusja kryptografów o braku unlinkability w ARF
- Discussion #211: „Cryptographers' Feedback on the EU Digital Identity's ARF" — formalna ocena architektury prywatności
- BBS# Protocol — Technical Report (Orange Innovation, June 2024) — protokół rozwiązujący unlinkability
- BBS# Protocol — Updated Version (March 2025) — pełne dowody bezpieczeństwa i benchmarki na smartfonach
Member discussion