'Your app(s) are using a WebView that is vulnerable to cross-app scripting
My android app keeps getting rejected because of this reason: Your app(s) are using a WebView that is vulnerable to cross-app scripting.
I already did an extensive search and found some thing I could do:
Follow the steps on https://support.google.com/faqs/answer/9084685. Since I'm using a launcher, I have to follow option 2.
I added this code in my manifest.xml: android.webkit.WebView.EnableSafeBrowsing
I'm sure the problem lies in the code posted below, where I don't use a fixed url for my loadURL, but I let it change via intent, being a notificiation url or a mail link. I'm aware this gives serious security issues, but I don't know how to fix it. In the link I provided above I want to follow option 2 but:
- I can't disable javascript (my webpage won't load without it)
- I don't know how to validate/secure the url in loadURL in such a way that Google allows me to upload my app to the Play Store.
Can someone please help me?
@Override
protected void onCreate(Bundle savedInstanceState) {
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("send-url"));
super.onCreate(savedInstanceState);
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.activity_main);
String default_url = "https://www.playday.be/app/";
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(task -> {
if (task.isSuccessful() && task.getResult() != null) {
String refreshedToken = task.getResult();
Log.v("newToken",refreshedToken);
sendRegistrationToServer(refreshedToken);
}
});
if(getIntent().getExtras() != null){
if (getIntent().getExtras().getString("pushUrl") != null) {
String url_from_notif = getIntent().getExtras().getString("pushUrl");
if(Patterns.WEB_URL.matcher(url_from_notif).matches()) {
default_url = url_from_notif;
Log.v("url_from_notif = ", default_url);
}
}
if (getIntent().getData() != null) {
String url_from_mail = getIntent().getData().toString();
url_from_mail = url_from_mail.replace("playday://", "");
if(Patterns.WEB_URL.matcher(url_from_mail).matches()) {
default_url = url_from_mail;
Log.v("url_from_mail = ", default_url);
}
}
}
Log.v("default_url_new = ", "" +default_url);
webView = findViewById(R.id.ifView);
assert webView != null;
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccess(true);
webSettings.setGeolocationEnabled(false);
webView.setWebViewClient(new Callback());
webView.setWebViewClient(new MyAppWebViewClient(){
public void onReceivedError(WebView webView, int errorCode, String description, String failingUrl) {
try {
webView.stopLoading();
} catch (Exception e) {
Log.v("method:", "onReceivedError");
}
if (webView.canGoBack()) {
webView.goBack();
}
webView.loadUrl("about:blank");
AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
alertDialog.setTitle("Je bent niet online...");
alertDialog.setIcon(R.mipmap.ic_launcher);
alertDialog.setMessage("Gelieve je internetconnectie te herstellen en probeer dan opnieuw.");
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Probeer opnieuw", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
startActivity(getIntent());
}
});
try {
alertDialog.show();
} catch(Exception e){
Log.e(TAG,"show dialog error no internet connection");
}
super.onReceivedError(webView, errorCode, description, failingUrl);
}
});
webView.loadUrl(default_url);
webView.setWebChromeClient(new WebChromeClient() {
public boolean onShowFileChooser(WebView view, ValueCallback<Uri[]> filePath, WebChromeClient.FileChooserParams fileChooserParams) {
Log.d(TAG,"111 ShowFileChooser For Android 5.0 ");
if (Build.VERSION.SDK_INT >= 23) {
if (mUMA != null) {
mUMA.onReceiveValue(null);
}
mUMA = filePath;
Log.d(TAG,"ShowFileChooser For Android 5.0 SDK_INT>=23 chk permission");
String[] PERMISSIONS = {android.Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.CAMERA};
if (!hasPermissions(mContext, PERMISSIONS)) {
ActivityCompat.requestPermissions((Activity) mContext, PERMISSIONS, REQUEST_CAMERA);
} else {
Log.d(TAG,"112 ShowFileChooser For Android 5.0 in IF SDK_INT>=23 permission grant");
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCM);
} catch (Exception ex) {
// Error occurred while creating the File
Log.e(TAG, "Unable to create Image File 1", ex);
}
// Continue only if the File was successfully created
if (photoFile != null) {
mCM = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, FCR);
}
} else {
Log.d(TAG,"113 ShowFileChooser For Android 5.0 in else SDK_INT>=23");
if (mUMA != null) {
mUMA.onReceiveValue(null);
}
mUMA = filePath;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCM);
} catch (Exception ex) {
// Error occurred while creating the File
Log.e(TAG, "Unable to create Image File 2", ex);
}
// Continue only if the File was successfully created
if (photoFile != null) {
mCM = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, FCR);
}
// Double check that we don't have any existing callbacks
return true;
}
});
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
String channelId = "1";
String channel2 = "2";
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(channelId,
"Channel 1", NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setDescription("This is BNT");
notificationChannel.setLightColor(Color.RED);
notificationChannel.enableVibration(true);
notificationChannel.setShowBadge(true);
assert notificationManager != null; //edit 1402
notificationManager.createNotificationChannel(notificationChannel);
NotificationChannel notificationChannel2 = new NotificationChannel(channel2,
"Channel 2", NotificationManager.IMPORTANCE_MIN);
notificationChannel.setDescription("This is bTV");
notificationChannel.setLightColor(Color.RED);
notificationChannel.enableVibration(true);
notificationChannel.setShowBadge(true);
notificationManager.createNotificationChannel(notificationChannel2);
}
}
Solution 1:[1]
I finally figured it out myself and got my app approved in the app store. I just needed to validate/sanitize the url that was loaded in the webview.
I changed the code:
if(getIntent().getExtras() != null){
if (getIntent().getExtras().getString("pushUrl") != null) {
String url_from_notif = getIntent().getExtras().getString("pushUrl");
if(Patterns.WEB_URL.matcher(url_from_notif).matches()) {
default_url = url_from_notif;
Log.v("url_from_notif = ", default_url);
}
}
if (getIntent().getData() != null) {
String url_from_mail = getIntent().getData().toString();
url_from_mail = url_from_mail.replace("playday://", "");
if(Patterns.WEB_URL.matcher(url_from_mail).matches()) {
default_url = url_from_mail;
Log.v("url_from_mail = ", default_url);
}
}
}
To:
if(getIntent().getExtras() != null){
String url_from_notif = getIntent().getExtras().getString("pushUrl");
if (url_from_notif != null) {
if(URLUtil.isValidUrl(url_from_notif) && Patterns.WEB_URL.matcher(url_from_notif).matches()) {
default_url = url_from_notif;
Log.v("url_from_notif = ", default_url);
}
}
if (getIntent().getData() != null) {
String url_from_mail = getIntent().getData().toString();
url_from_mail = url_from_mail.replace("playday://", "");
if(URLUtil.isValidUrl(url_from_mail) && Patterns.WEB_URL.matcher(url_from_mail).matches()) {
default_url = url_from_mail;
Log.v("url_from_mail = ", default_url);
}
}
}
And:
webView.loadUrl(default_url);
To:
URI uri;
try {
uri = new URI(default_url);
String domain = uri.getHost();
if (!default_url.startsWith("https://www.playday.be") || !domain.equals("www.playday.be") || !URLUtil.isValidUrl(default_url) || !Patterns.WEB_URL.matcher(default_url).matches()) {
webView.loadUrl("about:blank");
} else {
webView.loadUrl(default_url);
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Simon Lenaerts |
