Dziś cofnijmy się mniej więcej o rok, konkretnie do tego wpisu: Jak zepsuć uwierzytelnienie w aplikacji mobilnej? A konkretnie do tego przypadku, w którym hasło (PIN) do aplikacji mobilnej było wykorzystywane do wygenerowania klucza używanego do szyfrowania danych przechowywanych lokalnie na urządzeniu. W opisywanym scenariuszu skutki takiego rozwiązania były tragiczne - kwestią sekund było odzyskanie kodu PIN potrzebnego do uwierzytelnienia się w aplikacji, oczywiście pod warunkiem, że ktoś wcześniej uzyskał dostęp do urządzenia ofiary.
Tym razem pokażę w jaki sposób zmodyfikować to rozwiązanie, by było odrobinę lepsze.
Na czym polega(ł) problem? Chodzi o to, że atakujący, który uzyskał dostęp do urządzenia ofiary, był w stanie ustalić jej PIN bez kontaktu z serwerem. Był w stanie to zrobić, ponieważ klucz używany do szyfrowania danych przechowywanych lokalnie był generowany na podstawie kodu PIN użytkownika. Atakujący sprawdzając wszystkie możliwe kody PIN (przestrzeń możliwości jest ograniczona) mógł znaleźć ten właściwy (klucz wygenerowany w oparciu o ten PIN powoduje poprawne rozszyfrowanie bazy).
Problemów w tym miejscu jest kilka, ale najbardziej istotne to:
- bardzo ograniczona ilość możliwych kodów PIN,
- jednoznaczne mapowanie między kluczem i PIN użytkownika,
Z pierwszym problemem nie jesteśmy sobie w stanie poradzić. Możemy natomiast roboczo założyć, że PIN ma długość sześciu cyfr, co daje milion możliwych kodów. Skupimy się natomiast na drugiej części problemu - przy pomocy prostego zabiegu możemy doprowadzić do tego, by mapowanie kluczy na PINy nie było już jednoznaczne. Oczywiście wszystko ma swój koszt, cudów nie ma.
Na początek proste pytanie - ile bitów potrzeba do zapisania 1 000 000 wartości? Odpowiedź - prawie 20 (prawie dokładnie 19.931568). Jeśli ograniczymy ilość możliwych kluczy do 2**16, będziemy mieli sytuację, w której jeden klucz może być wygenerowany w oparciu o 15 różnych kodów PIN.
Jak możemy to zrobić? Prosto. Wystarczy wyliczyć HASH(PIN) i wykorzystać pierwsze 16 bitów wyjścia. Następnie te 16 bitów można wykorzystać jako wejście do funkcji PBKDF2 (wraz z losowym, unikalnym dla urządzenia salt). W ten sposób uzyskamy klucz, który następnie będzie wykorzystywany do szyfrowania danych przechowywanych lokalnie na urządzeniu. Klucz ten zależy od salt oraz od PIN użytkownika. W praktyce jest on trywialny do ustalenia, bo salt musi być dostępny, a jedynym sekretem jest PIN, który ma niewielką ilość możliwych wartości dodatkowo zredukowaną przez "poświęcenie" czterech bitów.
Nasz atakujący w najgorszym razie po 2**16 próbach (czyli należy oczekiwać, że po 2**15) ustali prawidłowy klucz, a na tej podstawie - 15 możliwych kodów PIN. Jedynym sposobem sprawdzenia, czy PIN jest prawidłowy to próba wykorzystania tego kodu w trakcie uwierzytelnienia. Na tym etapie zadziałać mogą już standardowe mechanizmy, które blokują konto w przypadku określonej liczby nieudanych prób uwierzytelnienia. Jeśli konto jest jest blokowane po trzech próbach nieudanego uwierzytelnienia, atakujący ma 3/15 szansy na sukces.
Należałoby się dodatkowo zastanowić, jakie jest prawdopodobieństwo udanego ataku, w wyniku którego atakujący uzyskuje dostęp do plików znajdujących się na urządzeniu ofiary.
I dalsze tematy do rozważań:
- co się stanie, jeśli atakujący uzyska dostęp do informacji przechowywanych na urządzeniu,
- co się stanie, jeśli atakujący uzyska możliwość uwierzytelnienia w kontekście ofiary,
- czy atakujący uzyska możliwość wykonywania (dowolnych) operacji w kontekście ofiary.
Na podstawie tych informacji warto się zastanowić, czy aby na pewno te dane przechowywane lokalnie na urządzeniu są tak istotne, by je chronić poprzez szyfrowanie? Albo inaczej - skoro sposób generowania klucza powoduje, że atakujący i tak jest w stanie w ciągu kilku sekund znaleźć właściwy klucz, może lepszym pomysłem jest przechowywać klucz szyfrowania na urządzeniu, ale dzięki temu przynajmniej zredukować szanse atakującego na ustalenie kodu PIN wymaganego do logowania do 3/1000000?
P.S. Uzyskanie dostępu do systemu (bankowości mobilnej) w kontekście ofiary to jeszcze nie koniec świata. No chyba, że pozwoli to na wykonywanie operacji w kontekście ofiary, bo na przykład:
- operacja jest autoryzowana tym samym kodem PIN, który jest używany przy logowaniu,
- operacja jest autoryzowana przez SMS przychodzący na telefon, który jest już pod kontrolą atakującego.
Zawsze myślałem, że bit jest jednostką niepodzielną