Webview évite les alertes de sécurité de Google Play lors de l’implémentation de onReceivedSslError

J’ai un lien qui s’ouvrira dans webview. Le problème est qu’il ne peut pas être ouvert tant que je ne remplace pas onReceivedSslError comme ceci:

@Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); } 

Je reçois une alerte de sécurité de Google Play en disant:

Alerte de sécurité Votre application a une implémentation non sécurisée du gestionnaire WebViewClient.onReceivedSslError. Plus précisément, l’implémentation ignore toutes les erreurs de validation des certificates SSL, ce qui rend votre application vulnérable aux attaques d’interception. Un attaquant pourrait modifier le contenu de WebView affecté, lire des données transmises (telles que des identifiants de connexion) et exécuter du code dans l’application en utilisant JavaScript.

Pour gérer correctement la validation du certificate SSL, modifiez votre code pour appeler SslErrorHandler.proceed () chaque fois que le certificate présenté par le serveur répond à vos attentes et appelez SslErrorHandler.cancel () dans le cas contraire. Une alerte par courrier électronique contenant les applications et les classes concernées a été envoyée à l’adresse de votre compte de développeur.

Veuillez corriger cette vulnérabilité dès que possible et incrémenter le numéro de version de l’APK mis à niveau. Pour plus d’informations sur le gestionnaire d’erreurs SSL, consultez notre documentation dans le centre d’aide pour les développeurs. Pour d’autres questions techniques, vous pouvez publier sur https://www.stackoverflow.com/questions et utiliser les balises «android-security» et «SslErrorHandler». Si vous utilisez une bibliothèque tierce responsable de cela, veuillez en informer le 3ème partie et travailler avec eux pour résoudre le problème.

Pour confirmer que vous avez effectué la mise à niveau correctement, téléchargez la version mise à jour sur la console du développeur et revenez après cinq heures. Si l’application n’a pas été correctement mise à niveau, nous afficherons un avertissement.

Veuillez noter que, même si ces problèmes spécifiques ne concernent pas toutes les applications utilisant WebView SSL, il est préférable de restr à jour sur tous les correctifs de sécurité. Les applications présentant des vulnérabilités exposant les utilisateurs à un risque de compromission peuvent être considérées comme des produits dangereux en violation de la politique de contenu et de la section 4.4 du contrat de dissortingbution du développeur.

Veuillez vous assurer que toutes les applications publiées sont conformes à l’accord de dissortingbution des développeurs et à la politique de contenu. Si vous avez des questions ou des préoccupations, veuillez contacter notre équipe d’assistance via le centre d’aide aux développeurs Google Play.

Si je supprime onReceivedSslError (handler.proceed()) , la page ne s’ouvre pas.

Y a-t-il de toute façon je peux ouvrir la page dans Webview et éviter les alertes de sécurité.

Pour gérer correctement la validation du certificate SSL, modifiez votre code pour appeler SslErrorHandler.proceed () chaque fois que le certificate présenté par le serveur répond à vos attentes et appelez SslErrorHandler.cancel () dans le cas contraire.

Comme le courrier électronique l’a dit, onReceivedSslError devrait gérer les utilisateurs qui onReceivedSslError à une page avec un certificate non valide, comme une boîte de dialog de notification. Vous ne devriez pas procéder directement.

Par exemple, j’ajoute une boîte de dialog d’alerte pour que l’utilisateur ait confirmé et que Google ne montre plus d’avertissement.


 @Override public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) { final AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(R.ssortingng.notification_error_ssl_cert_invalid); builder.setPositiveButton("continue", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { handler.proceed(); } }); builder.setNegativeButton("cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { handler.cancel(); } }); final AlertDialog dialog = builder.create(); dialog.show(); } 

Plus d’expliquer sur l’e-mail.

Plus précisément, l’implémentation ignore toutes les erreurs de validation des certificates SSL, ce qui rend votre application vulnérable aux attaques d’interception.

L’e-mail indique que l’outil par défaut a ignoré un problème de sécurité SSL important. Nous devons donc le gérer dans notre propre application qui utilise WebView. Notifier l’utilisateur avec un dialog d’alerte est un moyen simple.

Le correctif qui fonctionne pour moi est juste de désactiver la fonction onReceivedSslError définie dans AuthorizationWebViewClient . Dans ce cas, handler.cancel sera appelé en cas d’erreur SSL. Cependant, cela fonctionne bien avec les certificates SSL One Drive. Testé sur Android 2.3.7, Android 5.1.

Selon Google Security Alert: l’implémentation non sécurisée de l’interface X509TrustManager , Google Play ne supportera pas X509TrustManager partir du 11 juillet 2016:

Bonjour Google Play Developer,

Vos applications répertoriées à la fin de cet e-mail utilisent une implémentation peu sûre de l’interface X509TrustManager. Plus précisément, l’implémentation ignore toutes les erreurs de validation de certificate SSL lors de l’établissement d’une connexion HTTPS à un hôte distant, ce qui rend votre application vulnérable aux attaques d’interception. Un attaquant pourrait lire les données transmises (telles que les identifiants de connexion) et même modifier les données transmises sur la connexion HTTPS. Si vous avez plus de 20 applications affectées sur votre compte, veuillez consulter la console du développeur pour obtenir une liste complète.

Pour gérer correctement la validation du certificate SSL, modifiez votre code dans la méthode checkServerTrusted de votre interface X509TrustManager personnalisée pour générer une exception CertificateException ou IllegalArgumentException chaque fois que le certificate présenté par le serveur ne répond pas à vos attentes. Pour des questions techniques, vous pouvez publier dans Stack Overflow et utiliser les tags «android-security» et «TrustManager».

Veuillez résoudre ce problème dès que possible et incrémenter le numéro de version de l’APK mis à niveau. À compter du 17 mai 2016, Google Play bloquera la publication de toute nouvelle application ou mise à jour contenant l’implémentation non sécurisée de l’interface X509TrustManager.

Pour confirmer que vous avez apporté les modifications correctes, envoyez la version mise à jour de votre application à la console du développeur et revenez au bout de cinq heures. Si l’application n’a pas été correctement mise à niveau, nous afficherons un avertissement.

Bien que ces problèmes spécifiques n’affectent pas chaque application avec l’implémentation de TrustManager, il est préférable de ne pas ignorer les erreurs de validation de certificate SSL. Les applications présentant des vulnérabilités exposant les utilisateurs à un risque de compromission peuvent être considérées comme des produits dangereux en violation de la politique de contenu et de la section 4.4 du contrat de dissortingbution du développeur.

Vous pouvez utiliser SslError pour show, quelques informations sur l’erreur de ce certificate, et vous pouvez écrire dans votre boîte de dialog la chaîne de l’erreur de type.

 @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { final SslErrorHandler handlerFinal; handlerFinal = handler; int mensaje ; switch(error.getPrimaryError()) { case SslError.SSL_DATE_INVALID: mensaje = R.ssortingng.notification_error_ssl_date_invalid; break; case SslError.SSL_EXPIRED: mensaje = R.ssortingng.notification_error_ssl_expired; break; case SslError.SSL_IDMISMATCH: mensaje = R.ssortingng.notification_error_ssl_idmismatch; break; case SslError.SSL_INVALID: mensaje = R.ssortingng.notification_error_ssl_invalid; break; case SslError.SSL_NOTYETVALID: mensaje = R.ssortingng.notification_error_ssl_not_yet_valid; break; case SslError.SSL_UNTRUSTED: mensaje = R.ssortingng.notification_error_ssl_untrusted; break; default: mensaje = R.ssortingng.notification_error_ssl_cert_invalid; } AppLogger.e("OnReceivedSslError handel.proceed()"); View.OnClickListener acept = new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); handlerFinal.proceed(); } }; View.OnClickListener cancel = new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); handlerFinal.cancel(); } }; View.OnClickListener listeners[] = {cancel, acept}; dialog = UiUtils.showDialog2Buttons(activity, R.ssortingng.info, mensaje, R.ssortingng.popup_custom_cancelar, R.ssortingng.popup_custom_cancelar, listeners); } 

J’avais besoin de vérifier notre magasin de confiance avant de montrer un message à l’utilisateur, donc j’ai fait ceci:

 public class MyWebViewClient extends WebViewClient { private static final Ssortingng TAG = MyWebViewClient.class.getCanonicalName(); Resources resources; Context context; public MyWebViewClient(Resources resources, Context context){ this.resources = resources; this.context = context; } @Override public void onReceivedSslError(WebView v, final SslErrorHandler handler, SslError er){ // first check certificatee with our truststore // if not trusted, show dialog to user // if trusted, proceed try { TrustManagerFactory tmf = TrustManagerUtil.getTrustManagerFactory(resources); for(TrustManager t: tmf.getTrustManagers()){ if (t instanceof X509TrustManager) { X509TrustManager trustManager = (X509TrustManager) t; Bundle bundle = SslCertificate.saveState(er.getCertificate()); X509Certificate x509Certificate; byte[] bytes = bundle.getByteArray("x509-certificatee"); if (bytes == null) { x509Certificate = null; } else { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes)); x509Certificate = (X509Certificate) cert; } X509Certificate[] x509Certificates = new X509Certificate[1]; x509Certificates[0] = x509Certificate; trustManager.checkServerTrusted(x509Certificates, "ECDH_RSA"); } } Log.d(TAG, "Certificate from " + er.getUrl() + " is trusted."); handler.proceed(); }catch(Exception e){ Log.d(TAG, "Failed to access " + er.getUrl() + ". Error: " + er.getPrimaryError()); final AlertDialog.Builder builder = new AlertDialog.Builder(context); Ssortingng message = "SSL Certificate error."; switch (er.getPrimaryError()) { case SslError.SSL_UNTRUSTED: message = "O certificado não é confiável."; break; case SslError.SSL_EXPIRED: message = "O certificado expirou."; break; case SslError.SSL_IDMISMATCH: message = "Hostname inválido para o certificado."; break; case SslError.SSL_NOTYETVALID: message = "O certificado é inválido."; break; } message += " Deseja continuar mesmo assim?"; builder.setTitle("Erro"); builder.setMessage(message); builder.setPositiveButton("Sim", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { handler.proceed(); } }); builder.setNegativeButton("Não", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { handler.cancel(); } }); final AlertDialog dialog = builder.create(); dialog.show(); } } } 

Les solutions proposées jusqu’ici ne font que contourner le contrôle de sécurité, elles ne sont donc pas sûres.

Ce que je suggère, c’est d’incorporer le ou les certificates dans l’application et, lorsqu’une erreur SslError se produit, vérifiez que le certificate du serveur correspond à l’un des certificates incorporés.

Alors, voici les étapes:

  1. Récupérez le certificate sur le site Web.

    • Ouvrir le site sur Safari
    • Cliquez sur l’icône du cadenas près du nom du site
    • Cliquez sur Afficher le certificate
    • Faites glisser et déposez le certificate dans un dossier

voir https://www.markbrilman.nl/2012/03/howto-save-a-certificatee-via-safari-on-mac/

  1. Copiez le certificate (fichier .cer) dans le dossier res / raw de votre application

  2. Dans votre code, chargez le ou les certificates en appelant loadSSLCertificates ()

     private static final int[] CERTIFICATES = { R.raw.my_certificatee, // you can put several certificatees }; private ArrayList certificatees = new ArrayList<>(); private void loadSSLCertificates() { try { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); for (int rawId : CERTIFICATES) { InputStream inputStream = getResources().openRawResource(rawId); InputStream certificateInput = new BufferedInputStream(inputStream); try { Certificate certificate = certificateFactory.generateCertificate(certificateInput); if (certificate instanceof X509Certificate) { X509Certificate x509Certificate = (X509Certificate) certificate; SslCertificate sslCertificate = new SslCertificate(x509Certificate); certificates.add(sslCertificate); } else { Log.w(TAG, "Wrong Certificate format: " + rawId); } } catch (CertificateException exception) { Log.w(TAG, "Cannot read certificate: " + rawId); } finally { try { certificateInput.close(); inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } catch (CertificateException e) { e.printStackTrace(); } } 
  3. Lorsqu’un SslError se produit, vérifiez que le certificate du serveur correspond à un certificate incorporé. Notez qu’il n’est pas possible de comparer directement les certificates. J’utilise donc SslCertificate.saveState pour mettre les données du certificate dans un ensemble, puis je compare toutes les entrées de l’ensemble.

     webView.setWebViewClient(new WebViewClient() { @Override public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) { // Checks Embedded certificatees SslCertificate serverCertificate = error.getCertificate(); Bundle serverBundle = SslCertificate.saveState(serverCertificate); for (SslCertificate appCertificate : certificatees) { if (TextUtils.equals(serverCertificate.toSsortingng(), appCertificate.toSsortingng())) { // First fast check Bundle appBundle = SslCertificate.saveState(appCertificate); Set keySet = appBundle.keySet(); boolean matches = true; for (Ssortingng key : keySet) { Object serverObj = serverBundle.get(key); Object appObj = appBundle.get(key); if (serverObj instanceof byte[] && appObj instanceof byte[]) { // key "x509-certificatee" if (!Arrays.equals((byte[]) serverObj, (byte[]) appObj)) { matches = false; break; } } else if ((serverObj != null) && !serverObj.equals(appObj)) { matches = false; break; } } if (matches) { handler.proceed(); return; } } } handler.cancel(); Ssortingng message = "SSL Error " + error.getPrimaryError(); Log.w(TAG, message); } }); 

Dans ma situation: cette erreur s’est produite lorsque nous avons essayé de mettre à jour apk téléchargé sur le Google Play Store et d’obtenir une erreur SSL: j’ai ensuite utilisé le code suivant

 private class MyWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, Ssortingng url) { view.loadUrl(url); return true; } @Override public void onPageFinished(WebView view, Ssortingng url) { try { progressDialog.dismiss(); } catch (WindowManager.BadTokenException e) { e.printStackTrace(); } super.onPageFinished(view, url); } @Override public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) { final AlertDialog.Builder builder = new AlertDialog.Builder(PayNPayWebActivity.this); builder.setMessage(R.ssortingng.notification_error_ssl_cert_invalid); builder.setPositiveButton("continue", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { handler.proceed(); } }); builder.setNegativeButton("cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { handler.cancel(); } }); final AlertDialog dialog = builder.create(); dialog.show(); } }