Categoria do OWASP: MASVS-CODE - Qualidade do código (link em inglês)
Visão geral
Um carregamento de URI não seguro ocorre quando um app Android não avalia corretamente a validade de um URI antes de carregá-lo em uma WebView.
O motivo desse tipo de vulnerabilidade é que um URI é composto por várias partes (link em inglês). Assim, ao menos o esquema e o host (da parte da autoridade) precisam ser verificados (por exemplo, na lista de permissões) antes de o URI ser carregado em uma WebView ou usado internamente pelo aplicativo.
Os erros mais comuns incluem:
- Verificar o host, mas não o esquema, permitindo que um invasor use esquemas como
http://
,content://
oujavascript://
com um host autenticado. - Falha ao analisar o URI de forma correta, especialmente nos casos em que ele é recebido como uma string.
- Validação do esquema, mas não do host (validação de host insuficiente).
Em relação ao último caso, isso geralmente ocorre quando o aplicativo precisa permitir
subdomínios arbitrários de um domínio principal. Portanto, mesmo que o nome do host tenha sido
extraído corretamente, o app usa métodos como startsWith
, endsWith,
ou
contains
da classe java.lang.String
para validar a presença de um domínio
principal na seção da string extraída. Usados incorretamente, esses métodos podem levar
a resultados com falha e forçar o aplicativo a confiar de forma indevida em um host potencialmente
mal-intencionado.
Impacto
Dependendo do contexto em que o host é usado, o impacto pode variar. Nos casos em que o carregamento de um URI malicioso (ou seja, que ignorou a filtragem/lista de permissões) em um WebView pode levar à invasão de conta (por exemplo, usando phishing), execução de código (por exemplo, carregamento de JavaScript malicioso) ou comprometendo o dispositivo (exploração do código entregue usando um hiperlink).
Mitigações
Ao processar URIs de string, é importante analisar a string como um URI e validar o esquema e o host:
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");
}
}
Para a validação do host, depois de isolar a parte do URI correspondente, é importante
fazer uma validação total (e não parcial) para identificar com precisão se
o host é confiável ou não. Ao usar métodos como startsWith
ou endsWith
,
é importante usar a sintaxe correta e não ignorar
caracteres ou símbolos necessários. Por exemplo, endsWith
exige o caractere de ponto ".
"
antes do nome de domínio para uma correspondência precisa. Ignorar esses
caracteres pode resultar em correspondências imprecisas e comprometer a segurança. Como
os subdomínios podem ser aninhados infinitamente, a correspondência de expressão regular não é uma
estratégia recomendada para validar nomes de host.
Colaboradores: Dimitrios Valsamaras e Michael Peck da Microsoft Threat Intelligence