O Google Authenticator

Jakiś czas temu Google udostępniło możliwość logowania się do swoich usług za pomocą uwierzytelnienia dwuskładnikowego. Jedną z możliwości jest wykorzystanie kodów jednorazowych generowanych przez aplikację Google Authenticator (źródła tego projektu można znaleźć tutaj: google-authenticator). Aplikacja ta jest niczym innym, jak programowym tokenem generującym hasła jednorazowe w oparciu o czas lub licznik (więcej szczegółów: HOTP: An HMAC-Based One-Time Password Algorithm).

Jakiś czas temu miało miejsce włamanie do RSA, w trakcie którego wykradziono “bliżej niesprecyzowane dane” związane z tokenami SecureID. Od początku przypuszczano, że te “bliżej niesprecyzowane dane” to seedy (np.: Co skradziono z RSA?). Ostatecznie RSA podjęła decyzję o wymianie wszystkich tokenów: Open Letter to RSA SecurID Customers), co w zasadzie potwierdziło wcześniejsze domysły.

Wróćmy do Google Authenticator. Jeśli ktoś przeanalizuje wspomniany wcześniej RFC, bez trudu zauważy, że kod jest generowany na podstawie klucza (seed) oraz jakichś dodatkowych danych (wskazanie czasowe, licznik, challenge). Jedynym sekretem w tym przypadku jest właśnie klucz, który należałoby chronić. Ujawnienie klucza pozwoli atakującemu generować hasła jednorazowe bez dodatkowych problemów w przypadku haseł opartych na czasie i z drobnym problemem w przypadku licznika, bo poza kluczem potrzebny jest jeszcze jego właściwy stan.

Tokeny “sprzętowe” są specjalizowanymi urządzeniami, które służą tylko do generowania kodów i niczego więcej. Nie ma prostej możliwości wyciągnięcia wartości seed z takiego urządzenia (są w pewnym stopniu tamper-resistant). Na takim urządzeniu nie można zainstalować również “obcego”, w szczególności złośliwego kodu. Nie można ich również niepostrzeżenie “zwielokrotnić” (no chyba, że posiada się seed), gdy token zmienia właściciela jego prawowity właściciel ma szansę dość szybko zorientować się, że go nie ma. Zupełnie inaczej przedstawia się sytuacja w przypadku tokenów programowych. Obecnie telefon staje się urządzeniem ogólnego przeznaczenia, na którym można zainstalować praktycznie cokolwiek. Ich odporność na ingerencję z zewnątrz jest niższa, niż w przypadku tokenu, odporność na takie ataki nie jest kluczową cechą telefonu (jeszcze?). Informacja przechowywana na telefonie może zostać niepostrzeżenie skopiowana, a jej właściciel może pozostać w błogim przekonaniu, że nadal jest jedynym jej posiadaczem.

Jak wygląda bezpieczeństwo klucza w przypadku Google Authenticator? No niestety, nie wygląda:

# cd /data/data/com.google.android.apps.authenticator/databases cd /data/data/com.google.android.apps.authenticator/databases # sqlite3 databases sqlite3 databases SQLite version 3.6.22 Enter “.help” for instructions Enter SQL statements terminated with a “;” sqlite> select * from accounts; select * from accounts; 1|test1|I234567I234567I234567|0|0 2|test2|I234567I234567I234567|2|1

sqlite> .schema accounts .schema accounts CREATE TABLE accounts (_id INTEGER PRIMARY KEY, email TEXT NOT NULL, secret TEXT NOT NULL, counter INTEGER DEFAULT 0, type INTEGER);

Tak, w kolumnie secret przechowywane jest to, czego potrzeba, by “sklonować” token (klucz w postaci Base32). Nie ma żadnego dodatkowego wejścia (danych pochodzących od użytkownika), wystarczy zawartość tej bazy. Co prawda sama baza danych jest chroniona mechanizmami dostępnymi w systemie (odpowiednie ACL), ale może to być niewystarczające choćby w przypadku, gdy:

Pisałem już o tym, że zabezpieczenie (szyfrowanie) danych na urządzeniach mobilnych jest trudnym zadaniem. Akurat w przypadku aplikacji typu Google Authenticator sprawa jest dużo łatwiejsza. Aplikacja generuje kod, który przesyłany jest do serwera w celu weryfikacji (w procesie uwierzytelnienia). O ile atakujący może mieć pełną kontrolę nad aplikacją i telefonem, na którym ta aplikacja działa, serwera już nie kontroluje. Serwer po swojej stronie może zablokować konto/token użytkownika jeśli stwierdzi, że ilość błędnych prób była zbyt duża. Atakujący nie ma żadnego innego sposobu, by przekonać się, że wygenerowany kod był prawidłowy, jak wysłać go do serwera (no dobrze, nie jest to do końca prawdą, może sprawdzać poprawność kodów offline pod warunkiem, że dysponuje kilkoma wcześniejszymi kodami wygenerowanymi przez użytkownika).

Jak można zabezpieczyć klucz? Cóż, w tym wypadku odpowiedź jest prosta – szyfrować klucz (sekret) za pomocą klucza generowanego na podstawie hasła (kodu PIN) wpisywanego przez użytkownika przy uruchomieniu aplikacji lub po dłuższej bezczynności. Token powinien generować kody dla każdego podanego przez użytkownika/atakującego kodu PIN (operacja rozszyfrowania sekretu powinna zakończyć się sukcesem). Atakujący musi użyć wygenerowanego kodu, by sprawdzić, czy jest prawidłowy (czyli pośrednio – czy podany PIN jest prawidłowy). Pomijając wspomniany przypadek specjalny, gdy atakujący dysponuje kilkoma wcześniej wygenerowanymi kodami, w celu takiej weryfikacji kod musi zostać przesłany do serwera, a serwer po określonej ilości nieprawidłowych kodów blokuje użytkownika/token. W rezultacie atakujący ma tylko kilka prób, by odgadnąć prawidłowy PIN.

Temat ten był rozważany w ramach projektu: Issue 5: Encrypt key on mobile devcies. Szczególnie interesujący jest komentarz #5, pozwolę sobie zacytować ważniejsze fragmenty:

This all really depends on your threat model. For the threat model of a remote attack on your phone, I don't believe encrypting the secret key on the phone actually improves security.
(...)
Or you believe that the phone's security is already compromised, in which case rogue applications can just as easily read the user's pin as they can read the secret key.

No właśnie, kluczowy tutaj jest threat model. Dobrze jest wiedzieć jakie zagrożenia (threats) zostały uwzględnione przy projektowaniu danego rozwiązania i nie oczekiwać, że zrobi ono coś więcej, niż to, do czego zostało zaprojektowane. W tym kontekście, że będzie się bronić przed czymś, przed czym bronić się nie może (tu konkretnie: malware). Można też zastanowić się, czy w modelu uwzględnić również drugi przypadek, o którym pisałem, czyli nieograniczony dostęp fizyczny do telefonu. Moim zdaniem – tak, a przynajmniej decyzja o wyłączeniu tego scenariusza spod rozważań powinna być uzasadniona. Z drugiej strony ta decyzja w szerszym kontekście nie jest specjalnie istotna i nie warto się upierać przy stosowaniu opisanego wcześniej zabezpieczenia. Temat pozostawię w zawieszeniu, każdy może mieć swoje zdanie, natomiast bardzo jestem ciekawy jak będziecie je (ewentualnie) uzasadniać.

A teraz przećwiczmy mały disaster recovery.

Dilbert.com

Załóżmy, że utraciłeś telefon. Jakie działania podejmiesz?

W tym wypadku nie chodzi o budżet, ale o uświadomienie sobie jakie konfitury są teraz na telefonie. I w jak wielu miejscach telefon jest “zaufany”.

Oryginał tego wpisu dostępny jest pod adresem O Google Authenticator

Autor: Paweł Goleń