Głębokie ukrycie doczekało się wpisu na Wikipedii. I wszystko byłoby w porządku, gdyby nie ten przykład:
http://example.com/29d9283aba927109a289b03812738d89201/2873944786672/10284.pdf
Wydaje mi się, że na temat trzeba spojrzeć nieco z szerszej perspektywy. A przy okazji - moim zdaniem głębokie ukrycie nie do końca zasługuje na oddzielny wpis w Wikipedii. Na dobrą sprawę jest to pewna forma security through obscurity, która w dodatku, w pewnych przypadkach i do pewnego czasu, działa.
Załóżmy, że mamy do czynienia z aplikacją, za której pośrednictwem każdy może sprawdzić swoje faktury za telefon. W pierwszym kroku należy uwierzytelnić się do aplikacji, a następnie pobrać plik PDF z interesującą nas fakturą.
Na początku mamy identyfikację (kim waść jesteś) i uwierzytelnienie (udowodnij waść, że jesteś tym, za kogo się podajesz) użytkownika (jeśli ktoś w mojej obecności użyje potworków językowych autentykacja lub autentyfikacja - pożałuje). Naturalnym celem atakującego będzie pozyskanie/odgadnięcie prawidłowej kombinacji nazwy użytkownika i hasła.
Załóżmy na chwilę, że nazwa użytkownika jest prosta do ustalenia (niech będzie to numer telefonu), do odgadnięcia pozostanie więc hasło. A hasłem tym niech będzie (tak jak w przypadku jednej z sieci) zestaw 4 + 8 cyfr (forma uwierzytelnienia dwuskładnikowego, gdzie pierwsze 4 cyfry są statyczne, pozostałe 8 cyfr to hasło dynamiczne), przy czym w dodatku obie części można zgadywać oddzielnie. W pierwszym etapie można ustalić statyczną część hasła, następnie należy próbować odgadnąć właściwe w danej chwili hasło dynamiczne. Hasło statyczne ma 10 000 możliwych wartości, hasło dynamiczne 100 000 000. Jeśli nie ma żadnych mechanizmów mających na celu ograniczenie tempa łamania hasła (czas trwania pojedynczej próby uwierzytelnienia, blokowanie konta, ...), atakujący wcześniej lub później uzyska dostęp do konta swojej ofiary.
Ile czasu mu to zajmie? Można próbować to oszacować. Istotną pomocą jest tu fakt, że można oddzielnie ustalić statyczną część hasła i później szukać tylko części dynamicznej. Daje to 10**4 + 10**8 prób. I tu warto się chwilę zatrzymać, zwłaszcza przy tej części 10**8. Zakładam, że dla każdej próby generowane jest nowe hasło jednorazowe, więc nie jest gwarantowane, że właściwe hasło zostanie znalezione w 10**8 prób. Wiemy tylko, że w każdej próbie jest szansa 1/10**8 na trafienie właściwego hasła. Dla porównania prawdopodobieństwo trafienia 6 w lotto to trochę lepiej niż 1/14 000 000 (patrz: kombinacja bez powtórzeń). Chwilowo przyjmijmy jednak optymistyczny sposób postrzegania świata i załóżmy, że mamy gwarancję, że maksymalnie w 10**8 (trochę mniej niż 2**27) poznamy hasło właściwe hasło dynamiczne i uzyskamy w ten sposób dostęp do konta ofiary. Wystarczy przemnożyć czas potrzebny na jedną próbę uwierzytelnienia i (...)
Po uwierzytelnieniu pojawia się identyfikator sesji, który zastępuje dane uwierzytelniające (nazwa użytkownika + hasło statyczne + hasło dynamiczne). Obecnie identyfikator sesji ma zwykle długość 128 bitów przy entropii ponad 100 bitów. Szansa na odgadnięcie identyfikatora sesji jest więc (w przybliżeniu) jak 1 do 2**100. Jest to więc dużo trudniejsze, niż zgadnięcie właściwych danych uwierzytelniających. Dodatkowo trzeba pamiętać, że:
- w danej chwili aktywna jest zwykle więcej niż jedna sesja,
- identyfikator sesji jest ważny jedynie przez pewien czas (od uwierzytelnienia do zamknięcia lub wygaśnięcia sesji),
To, że aktywna jest więcej niż jedna sesja powoduje, że trafienie prawidłowego identyfikatora może być nieco łatwiejsze, niż to 2**100. Oczywiście mowa o jakiejś prawidłowej sesji, a nie prawidłowej sesji należącej konkretnie do użytkownika, którego dane chcemy poznać. Nawet jeśli identyfikator sesji jest trywialny do odgadnięcia nie można przejąć czegoś, czego nie ma. W tym wypadku nie można przejąć sesji użytkownika, jeśli użytkownik nie jest zalogowany.
Idźmy dalej. Jesteśmy uwierzytelnieni w aplikacji i pobieramy wybrany rachunek. Załóżmy, że nie ma kontroli dostępu. Niech będzie jeszcze gorzej, załóżmy, że nie tylko użytkownik A może pobierać rachunki użytkownika B. Niech rachunki można pobrać anonimowo, bez żadnego uwierzytelnienia. Jedynym "zabezpieczeniem" przed pobraniem cudzego rachunku jest w takim przypadku właściwa "ścieżka", pod którą ten rachunek jest dostępny.
W tej chwili należy dobrze przyjrzeć się temu jak taki "identyfikator" wygląda, jak jest konstruowany. Jeśli będzie miał postać:
<numer_telefonu>_<rok>_<miesiąc>.pdf
oczywiste jest, że pobranie dowolnego rachunku dla dowolnego numeru za dowolny okres rozliczeniowy, jest trywialne. Jedynym "wyzwaniem" jest ustalenie numeru telefonu.
Inaczej sytuacja przedstawia się, jeśli ścieżka wygląda tak, jak na początku wpisu. Tutaj "strzelanie w ciemno" może spowodować więcej problemów. Pierwsza część ścieżki zawiera 35 znaków z zakresu (prawdopodobnie) od 0 do F. Daje to 16**35 możliwości (2**140). Druga część to 13 znaków z zakresu 0-9, co z kolei daje 10**13 możliwości (mniej więcej 2**43), ostatnia część to 10**5 możliwości (mniej niż 2**17). Jeśli porównać te wartości (wystarczy pierwsza część), to szansa na trafienie odpowiedniego identyfikatora zasobu jest zdecydowanie mniejsza, niż na odgadnięcie hasła lub identyfikatora sesji. Pokazaniu tego faktu służyło określanie ilości możliwych wartości jakie przyjąć mogą dane uwierzytelniające, identyfikator sesji oraz identyfikator zasobu.
Z faktu, że długość identyfikatora (lub jego części) oraz wykorzystanej przestrzeni znaków wynika tyle możliwości, wcale nie znaczy, że rzeczywiście tyle tych możliwości zostanie wykorzystanych. Dlatego w przypadku tak konstruowanych "identyfikatorów" trzeba zbadać jak one się zmieniają, czym mogą być poszczególne części. Może się okazać, że mimo teoretycznie ogromnej ilości możliwych wartości, w praktyce ilość tych możliwości jest zdecydowanie mniejsza. Może się również okazać, że identyfikatorów nie trzeba będzie zgadywać, bo dostępny będzie listing katalogów. Może się okazać jeszcze wiele innych rzeczy, w wyniku których okaże się, że efektywne zgadywanie identyfikatorów zasobów jest możliwe.
Może się również okazać, że zabezpieczenie oparte o "głębokie ukrycie" jest jednak wystarczające. Sam kilka razy spotkałem się z aplikacjami, w których kontrola dostępu pozostawiała wiele do życzenia, w zasadzie była realizowana wyłącznie w GUI (użytkownik widział tylko swoje obiekty, stary przykład: Lekcja 2: Nieprawidłowa kontrola dostępu do danych). Po analizie okazywało się jednak, że szansa na trafienie odpowiedniego identyfikatora obiektu, tak, by dostać się do cudzych danych, była wielokrotnie mniejsza niż szansa na to, że odgadnę identyfikator sesji ofiary, lub jego hasło. Nie jest to może rozwiązanie poprawne, ale w pewnych przypadkach - skuteczne.
Jako "głębokie ukrycie" można potraktować też mechanizm udostępniania zdjęć przez "linka", co wygląda tak:
https://plus.google.com/photos/10xxxxxxxxxxxxxxxxx48/albums/514yyyyyyyyyyyyy889?authkey=CNKujbzQ-JuzWQ
W tym wypadku, by uzyskać dostęp do udostępnionego albumu trzeba podać w adresie:
- prawidłowy identyfikator użytkownika,
- prawidłowy identyfikator albumu,
- prawidłową wartość authkey,
Jak w każdym przypadku systemu, którego działanie opiera się na tajności, całość działa do czasu, gdy sekrety nie zostaną celowo lub przypadkiem ujawnione. Lub do czasu, gdy ktoś ich nie zgadnie.
Dla mnie pojęcie "głębokie ukrycie" jest określeniem pejoratywnym. Rozumiem pod nim przede wszystkim sytuację, w której z powodu różnych (najczęściej głupich) błędów pewne dane/informacje zostały upublicznione. Może to wyglądać tak, lub tak, lub tak, jak zrobiłem w tym wpisie :) Powodzenia!
Order Allow,Deny
Deny from All
Domyślam się, że w tym przypadku zakładasz istnienie innego mechanizmu dostępu do plików dla uprawnionych użytkowników, który to mechanizm realizuje kontrolę dostępu. Wówczas Twój komentarz jest jak najbardziej na miejscu, przy czym równie dobrym zaleceniem jest trzymanie plików poza katalogiem serwera WWW