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://
lubjavascript://
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