Prefer java.net.URI than android.net.Uri
android.net.Uri has a known problem, it doesn't recognize \
as /, and this
incomplete host check is able to bypass it and load arbitrary pages in a built-in WebView
An android app might have WebView to open some web URL inside it, and these URLs might come from notifications, in-app campaigns, or external intents.
For example, I have an Activity which will handle Intent with pre-defined Extral data, and extract the URL. That means others could create Intent and send it to our app to handle.
// Sample Intent
Intent intent = new Intent();intent.setClassName("com.bramyeh.workshop", "com.bramyeh.NoSecureActivity");intent.putExtra("intent_content", "{\"data\":{\"type\":\"URI\",\"uri\":\"https:\\/\\/www.fack.website.com\\\\@www.true.website.com\\/..\\/headers/\"}}");
And then the app will verify the URL is valid or not. In my example, the URL is https://www.fack.website.com\@www.true.website.com/../headers/, and this URL is invalid because the host, www.fack.website.com, is not on our whitelist.
What will happen when I parse this URL by android.net.Uri? This Android parser android.net.Uri
has a known problem, it doesn't recognize \
as /
, but when it's loaded in a WebView, all backslashes are replaced with slashes, so it allows us to bypass those checks.
// there is no warnning or error
val uri = Uri.parse(“https://www.fack.website.com\@www.true.website.com/../headers/”)Log.d(TAG, uri.host) // www.true.website.com
So you will see the host coming from Uri.parse() will be www.true.website.com, not www.fack.website.com.
And you regard this URL is valid and then pass it into the webview. This incomplete host check is able to bypass it and load arbitrary pages in a built-in WebView and a third-party URL will be loaded with access to internal JS interfaces.
To fix the bug, we can use java.net.URI
, it will throw URISyntaxException when the invalid Uri is provided.
try {
// URI will throw URISyntaxException
val uri = URI(“https://www.fack.website.com\@www.true.website.com/../headers/”)
....
} catch (e: URISyntaxException) {
e.printStackTrace()
}