Widoki internetowe – niebezpieczne wczytywanie identyfikatorów URI

Kategoria OWASP: MASVS-CODE: jakość kodu

Omówienie

Niebezpieczne wczytywanie identyfikatora URI występuje, gdy aplikacja na Androida nieprawidłowo ocenia ważność identyfikatora URI przed wczytaniem go do komponentu WebView.

Podstawową przyczyną tego typu podatności jest to, że identyfikator URI składa się z wielu części, z których co najmniej schemat i host (części autoryzacyjnej) muszą zostać zweryfikowane (np. dodane do listy dozwolonych), zanim identyfikator URI zostanie wczytany do widoku WebView lub użyty wewnętrznie przez aplikację.

Najczęstsze błędy to:

  • Sprawdzanie hosta, ale nie schematu, co umożliwia atakującemu używanie schematów takich jak http://, content:// lub javascript:// z uwierzytelnionym hostem.
  • Nieprawidłowe parsowanie URI, zwłaszcza w przypadkach, gdy URI jest odbierany jako ciąg znaków.
  • Sprawdzanie schematu, ale nie hosta (niewystarczająca weryfikacja hosta).

W ostatnim przypadku dzieje się tak zwykle wtedy, gdy aplikacja musi zezwalać na dowolne subdomeny domeny głównej. Nawet jeśli nazwa hosta została wyodrębniona prawidłowo, aplikacja używa metod takich jak startsWith, endsWith, lub contains klasy java.lang.String, aby sprawdzić, czy w wyodrębnionym ciągu znaków znajduje się domena główna. Nieprawidłowe użycie tych metod może prowadzić do błędnych wyników i zmuszać aplikację do nieprawidłowego zaufania potencjalnie złośliwemu hostowi.

Wpływ

W zależności od kontekstu, w jakim jest używany host, wpływ może być różny. W przypadkach, gdy załadowanie złośliwego identyfikatora URI (czyli takiego, który ominął filtrowanie lub białą listę) w komponencie WebView może potencjalnie doprowadzić do przejęcia konta (np. w wyniku phishingu), wykonania kodu (np. załadowania złośliwego kodu JavaScript) lub naruszenia bezpieczeństwa urządzenia (wykorzystanie kodu dostarczonego za pomocą hiperlinku).

Środki ograniczające ryzyko

Podczas obsługi identyfikatorów URI w postaci ciągów znaków ważne jest, aby przeanalizować ciąg znaków jako identyfikator URI i sprawdzić poprawność zarówno schematu, jak i hosta:

Kotlin

fun isUriTrusted(incomingUri: String, trustedHostName: String): Boolean {
    try {
        val uri = Uri.parse(incomingUri)
        return uri.scheme == "https" && uri.host == trustedHostName
    } catch (e: NullPointerException) {
        throw NullPointerException("incomingUri is null or not well-formed")
    }
}

Java

public static boolean isUriTrusted(String incomingUri, String trustedHostName)
    throws NullPointerException {
        try {
            Uri uri = Uri.parse(incomingUri);
            return uri.getScheme().equals("https") &&
            uri.getHost().equals(trustedHostName);
        } catch (NullPointerException e) {
            throw new NullPointerException(
                "incomingUri is null or not well-formed");
        }
    }

W przypadku weryfikacji hosta po wyodrębnieniu odpowiedniej części identyfikatora URI ważne jest, aby zweryfikować ją w całości (a nie tylko częściowo), aby dokładnie określić, czy host jest zaufany. Jeśli nie można uniknąć stosowania metod takich jak startsWith czy endsWith, ważne jest, aby używać prawidłowej składni i nie pomijać niezbędnych znaków lub symboli (np. w przypadku endsWith przed nazwą domeny wymagany jest znak „.”, aby uzyskać dokładne dopasowanie). Ignorowanie tych znaków może prowadzić do niedokładnych dopasowań i zagrażać bezpieczeństwu. Poddomeny mogą być zagnieżdżone w nieskończoność, dlatego dopasowywanie wyrażeń regularnych nie jest zalecaną strategią weryfikacji nazw hostów.

Współtwórcy: Dimitrios Valsamaras i Michael Peck z Microsoft Threat Intelligence

Materiały