threats.pl > Bezpieczeństwo aplikacji internetowych > Lekcja 2: Nieprawidłowa kontrola dostępu do danych

Lekcja 2: Nieprawidłowa kontrola dostępu do danych

Bardzo często spotykaną klasą błędów w aplikacjach internetowych są błędy kontroli dostępu do funkcji oraz do danych. W tej lekcji tematem będzie "kontrola dostępu do danych", choć przy okazji poruszonych będzie kilka innych kwestii.

Jak (często) realizowana jest "kontrola dostępu" do danych

Kontrola dostępu do danych (do funkcji zresztą też) jest często realizowana wyłącznie na poziomie GUI. Oznacza to, że funkcja generująca stronę sprawdza, do jakich danych użytkownik ma dostęp. W niektórych przypadkach takie rozwiązanie może być wystarczające, jednak typowym scenariuszem jest przygotowanie w ten sposób listy elementów (np. listy tytułów, listy użytkowników, listy rachunków bankowych (...)), na których użytkownik może wykonywać różne operacje, w szczególności może zażądać wyświetlenia szczegółów dotyczących danego obiektu/elementu. Programiści często zakładają, że jedynymi zdarzeniami, jakie jest w stanie wygenerować użytkownik są te, które pozwala wygenerować mu strona. Oczekują więc, że jeśli wypisane zostaną obiekty o określonych identyfikatorach (te, do których użytkownik ma dostęp) ze strony użytkownika nie przyjdzie żądanie z identyfikatorem obiektu, który nie był dostępny na tej liście. A to oczywiście jest błędne założenie.

Powołując się na listę 2009 CWE/SANS Top 25 Most Dangerous Programming Errors tego typu rozwiązania można zaliczyć do tej kategorii: CWE-602: Client-Side Enforcement of Server-Side Security.

Identyfikatory globalne są ZŁE

Z reguły każdy z rekordów w bazie danych ma swój unikalny identyfikator. "Aż się prosi", by wykorzytać go jako identyfikator w aplikacji internetowej. Nie zawsze jest to dobre rozwiązanie, choć nie zawsze wykorzystanie takiego identyfikatora jest błędem. Jesli dla przykładu identyfikator ten określa artykuł, przy czym z założenia wszystkie artykuły są dostępne dla każdego użytkownika, to zagrożenie ujawnienia danych w wyniku manipulacji tym identyfikatorem w tym wypadku nie istnieje - wszystkie informacje są jawne, więc o jakim ujawnieniu można tu mówić? Z drugiej strony, gdy w aplikacji jest wielu użytkowników, używanie takich globalnych identyfikatorów może spowodować i często powoduje błędy kontroli dostępu. Patrząc z kolei na OWASP TOP10 (2007) tego typu praktyki klasyfikują się do pozycji A4 - Insecure Direct Object Reference.

O identyfikatorach globalnych wiele razy pisałem na blogu:

Zaprezentowałem też przykładowe rozwiązanie realizujące kodowanie i dekodowanie identyfikatorów globalnych:

Moje rozwiązanie "kodowania" identyfikatorów to wyłączenie PoC.

Praktyczny przykład

Podobnie jak poprzednio, tym razem również przygotowałem prosty skrypt, na którym można sobie poćwiczyć. Dostępny jest on pod adresem: http://bootcamp.threats.pl/lesson02/.

Na przykładowej stronie po kliknięciu linku bądź wybraniu elementu z listy dropdown wyświetlana jest treść wybranej wiadomości. Elementy prezentowane na liście linków lub w menu dropdown stworzone są na podstawie zapytania do bazy danych, które wygląda następująco:

SELECT id, title, message FROM news WHERE public=1
    

Jak widać na listach wyświetlane są wyłącznie elementy oznaczone jako publiczne. Zapytanie wyświetlające szczegóły wygląda natomiast tak:

SELECT title, message FROM news WHERE id=?
    

Oczywiście za pytajnik podstawiany jest identyfikator wiadomości przekazany w parametrze id. Jak widać przy wyświetlaniu szczegółów danej wiadomości nie jest uwzględniony warunek, że wskazany identyfikator musi dotyczyć wiadomości publicznej. W rezultacie podając identyfikatory z poza dostępnej listy (nie jest raczej trudno zgadnąć jakie) uzyskuje się dostęp do wiadomości, które nie zostały oznaczone jako publiczne.

Zadanie do wykonania: przeczytać wiadomości, które nie są oznaczone jako publiczne. Zadanie to należy wykonać zarówno w przypadku, gdy parametr id przekazywany jest w URL (przekazywanie przez GET), jak i w przypadku pola dropdown, gdzie parametr ten przesyłany jest za pomocą metody POST.

Zobacz też

Podsumowanie

Po tym ćwiczeniu powinieneś wiedzieć: