WebView: So vermeiden Sie Sicherheitswarnungen von Google Play bei der Implementierung von onReceivedSslError

Lesezeit: 13 Minuten

Benutzer-Avatar
Kapitändroide

Ich habe einen Link, der sich öffnet WebView. Das Problem ist, dass es nicht geöffnet werden kann, bis ich es überschreibe onReceivedSslError so was:

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

Ich erhalte eine Sicherheitswarnung von Google Play, die besagt:

Sicherheitswarnung Ihre Anwendung verfügt über eine unsichere Implementierung des WebViewClient.onReceivedSslError-Handlers. Insbesondere ignoriert die Implementierung alle SSL-Zertifikatsvalidierungsfehler, wodurch Ihre App anfällig für Man-in-the-Middle-Angriffe wird. Ein Angreifer könnte den Inhalt des betroffenen WebViews ändern, übertragene Daten (z. B. Anmeldeinformationen) lesen und Code innerhalb der App mithilfe von JavaScript ausführen.

Um die SSL-Zertifikatvalidierung ordnungsgemäß zu handhaben, ändern Sie Ihren Code so, dass SslErrorHandler.proceed() immer dann aufgerufen wird, wenn das vom Server präsentierte Zertifikat Ihren Erwartungen entspricht, und rufen Sie andernfalls SslErrorHandler.cancel() auf. Eine E-Mail-Benachrichtigung mit den betroffenen Apps und Klassen wurde an die Adresse Ihres Entwicklerkontos gesendet.

Bitte beheben Sie diese Schwachstelle so schnell wie möglich und erhöhen Sie die Versionsnummer des aktualisierten APK. Weitere Informationen zum SSL-Fehlerhandler finden Sie in unserer Dokumentation im Developer Help Center. Bei anderen technischen Fragen können Sie auf https://www.stackoverflow.com/questions posten und die Tags „android-security“ und „SslErrorHandler“ verwenden. Wenn Sie eine Bibliothek eines Drittanbieters verwenden, die dafür verantwortlich ist, benachrichtigen Sie bitte den Drittanbieter und arbeiten Sie mit ihm zusammen, um das Problem zu beheben.

Um zu bestätigen, dass Sie das Upgrade korrekt durchgeführt haben, laden Sie die aktualisierte Version in die Developer Console hoch und versuchen Sie es nach fünf Stunden erneut. Wenn die App nicht korrekt aktualisiert wurde, zeigen wir eine Warnung an.

Bitte beachten Sie, dass diese spezifischen Probleme möglicherweise nicht jede App betreffen, die WebView SSL verwendet, es am besten ist, sich über alle Sicherheitspatches auf dem Laufenden zu halten. Apps mit Schwachstellen, die Benutzer dem Risiko einer Kompromittierung aussetzen, können als gefährliche Produkte angesehen werden, die gegen die Inhaltsrichtlinie und Abschnitt 4.4 der Entwicklervertriebsvereinbarung verstoßen.

Bitte stellen Sie sicher, dass alle veröffentlichten Apps der Vereinbarung für den Entwicklervertrieb und den Inhaltsrichtlinien entsprechen. Wenn Sie Fragen oder Bedenken haben, wenden Sie sich bitte über das Google Play Developer Help Center an unser Support-Team.

Wenn ich entferne onReceivedSslError (handler.proceed())dann öffnet sich die Seite nicht.

Kann ich die Seite irgendwie öffnen? WebView und Sicherheitsalarm vermeiden?

  • Die Idee ist, dass Sie das untersuchen sollen SSLCertificate innerhalb der SSLError und stellen Sie fest, ob dies tatsächlich ein gültiges Zertifikat für den Server ist, den Sie treffen. Dann, und nur dann, rufst du an proceed(). Sonst rufst du an cancel(). Es wäre hilfreich, wenn Sie ein reproduzierbares Minimalbeispiel oder zumindest eine URL angeben könnten, die diesen Rückruf auslöst.

    – CommonsWare

    21. März 2016 um 14:53 Uhr

  • Um SslError korrekt zu handhaben und zu überprüfen, überprüfen Sie diese Antwort: stackoverflow.com/a/49674821/1805520

    – Yoda066

    3. Juni 2020 um 17:16 Uhr

Benutzer-Avatar
sakiM

Um die SSL-Zertifikatvalidierung ordnungsgemäß zu handhaben, ändern Sie Ihren Code so, dass SslErrorHandler.proceed() immer dann aufgerufen wird, wenn das vom Server präsentierte Zertifikat Ihren Erwartungen entspricht, und rufen Sie andernfalls SslErrorHandler.cancel() auf.

Wie die E-Mail sagte, onReceivedSslError sollte verarbeiten, dass der Benutzer zu einer Seite mit ungültigem Zertifikat geht, z. B. ein Benachrichtigungsdialog. Sie sollten nicht direkt fortfahren.

Zum Beispiel füge ich einen Warndialog hinzu, um den Benutzer zu bestätigen, und es scheint, dass Google keine Warnung mehr anzeigt.


@Override
public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
    final AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage(R.string.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();
}

Erklären Sie mehr über die E-Mail.

Insbesondere ignoriert die Implementierung alle SSL-Zertifikatsvalidierungsfehler, wodurch Ihre App anfällig für Man-in-the-Middle-Angriffe wird.

Die E-Mail besagt, dass die Standardimplementierung ein wichtiges SSL-Sicherheitsproblem ignoriert hat. Also müssen wir es in unserer eigenen App handhaben, die WebView verwendet. Das Benachrichtigen des Benutzers mit einem Warndialog ist eine einfache Möglichkeit.

  • Die E-Mail sagt eigentlich nicht, dass wir den Benutzer über irgendetwas informieren sollten, liege ich falsch?

    – älterer Gott

    9. September 2016 um 2:30 Uhr

  • In meinem Fall implementiere ich dies wie @sakiM sagte. Aber Google hat trotzdem abgelehnt. Jemand mir andere Lösungen?

    – Bao-Hauptquartier

    20. Januar 2017 um 2:14 Uhr


  • @BaulHoa, indem Sie zeigen, dass der obige Dialog für mich funktioniert, überprüfen Sie bitte Ihren Code sorgfältig, möglicherweise gibt es noch Code, um den zu handhaben onReceiveSslError(). Haben Sie dieselbe E-Mail (wie ich) von Google erneut erhalten?

    – Kapitänsdroide

    2. April 2017 um 14:11 Uhr

  • final AlertDialog.Builder builder = new AlertDialog.Builder(this); Es akzeptiert “this” nicht als Parameter, da ich diesen Code in SystemWebViewClient.java (Cordova-App) kopiert habe. Was muss ich also hier übergeben?

    – Nik

    12. April 2017 um 6:54 Uhr


  • Gibt es eine Möglichkeit, dies ohne einen Warndialog zu lösen? Ist es möglich, ein Zertifikat für die Webansicht vom Server hinzuzufügen?

    – Lebensmacher

    28. Februar 2018 um 5:09 Uhr

Die bisher vorgeschlagenen Lösungen umgehen nur die Sicherheitsüberprüfung, sind also nicht sicher.

Was ich vorschlage, ist, das/die Zertifikat(e) in die App einzubetten, und wenn ein SslError auftritt, zu überprüfen, ob das Serverzertifikat mit einem der eingebetteten Zertifikate übereinstimmt.

Hier also die Schritte:

  1. Rufen Sie das Zertifikat von der Website ab.

    • Öffnen Sie die Website in Safari
    • Klicken Sie auf das Vorhängeschloss-Symbol neben dem Namen der Website
    • Klicken Sie auf Zertifikat anzeigen
    • Ziehen Sie das Zertifikat in einen Ordner und legen Sie es dort ab

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

  1. Kopieren Sie das Zertifikat (CER-Datei) in den res/raw-Ordner Ihrer App

  2. Laden Sie in Ihrem Code das/die Zertifikat(e), indem Sie loadSSLCertificates() aufrufen.

    private static final int[] CERTIFICATES = {
            R.raw.my_certificate,   // you can put several certificates
    };
    private ArrayList<SslCertificate> certificates = 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. Wenn ein SslError auftritt, überprüfen Sie, ob das Serverzertifikat mit einem eingebetteten Zertifikat übereinstimmt. Beachten Sie, dass es nicht möglich ist, Zertifikate direkt zu vergleichen, daher verwende ich SslCertificate.saveState, um die Zertifikatsdaten in ein Bündel zu packen, und vergleiche dann alle Bündeleinträge.

    webView.setWebViewClient(new WebViewClient() {
    
        @Override
        public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
    
            // Checks Embedded certificates
            SslCertificate serverCertificate = error.getCertificate();
            Bundle serverBundle = SslCertificate.saveState(serverCertificate);
            for (SslCertificate appCertificate : certificates) {
                if (TextUtils.equals(serverCertificate.toString(), appCertificate.toString())) { // First fast check
                    Bundle appBundle = SslCertificate.saveState(appCertificate);
                    Set<String> keySet = appBundle.keySet();
                    boolean matches = true;
                    for (String key : keySet) {
                        Object serverObj = serverBundle.get(key);
                        Object appObj = appBundle.get(key);
                        if (serverObj instanceof byte[] && appObj instanceof byte[]) {     // key "x509-certificate"
                            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();
            String message = "SSL Error " + error.getPrimaryError();
            Log.w(TAG, message);
        }
    
    
    });
    

Ich musste unseren Truststore überprüfen, bevor ich dem Benutzer eine Nachricht anzeigen konnte, also habe ich Folgendes getan:

public class MyWebViewClient extends WebViewClient {
private static final String 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 certificate 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-certificate");
                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);
        String 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();
    }
}
}

  • was ist TrustManagerUtil .wie man es implementiert

    – ShriKant A

    23. Februar 2021 um 6:17 Uhr

  • Ähnlich:gist.github.com/iammert/…

    – AmandaHLA

    24. Februar 2021 um 15:28 Uhr

Benutzer-Avatar
Naveen Prinz P

Fix, der für mich funktioniert, ist einfach zu deaktivieren onReceivedSslError Funktion definiert in AuthorizationWebViewClient. In diesem Fall handler.cancel wird im Falle eines SSL-Fehlers aufgerufen. Es funktioniert jedoch gut mit One Drive SSL-Zertifikaten. Getestet auf Android 2.3.7, Android 5.1.

Benutzer-Avatar
satish baddam

Entsprechend Google Security Alert: Unsichere Implementierung der Schnittstelle X509TrustManagerGoogle Play wird nicht unterstützt X509TrustManager ab 11. Juli 2016:

Hallo Google Play-Entwickler,

Ihre am Ende dieser E-Mail aufgeführten Apps verwenden eine unsichere Implementierung der Schnittstelle X509TrustManager. Insbesondere ignoriert die Implementierung alle SSL-Zertifikatsvalidierungsfehler beim Herstellen einer HTTPS-Verbindung zu einem Remotehost, wodurch Ihre App anfällig für Man-in-the-Middle-Angriffe wird. Ein Angreifer könnte übermittelte Daten (z. B. Anmeldedaten) mitlesen und sogar die auf der HTTPS-Verbindung übermittelten Daten verändern. Wenn Sie mehr als 20 betroffene Apps in Ihrem Konto haben, sehen Sie bitte in der Developer Console nach, um eine vollständige Liste zu erhalten.

Um die SSL-Zertifikatvalidierung ordnungsgemäß zu handhaben, ändern Sie Ihren Code in der checkServerTrusted-Methode Ihrer benutzerdefinierten X509TrustManager-Schnittstelle, um entweder CertificateException oder IllegalArgumentException auszulösen, wenn das vom Server präsentierte Zertifikat nicht Ihren Erwartungen entspricht. Bei technischen Fragen können Sie auf Stack Overflow posten und die Tags „android-security“ und „TrustManager“ verwenden.

Bitte beheben Sie dieses Problem so schnell wie möglich und erhöhen Sie die Versionsnummer des aktualisierten APK. Ab dem 17. Mai 2016 blockiert Google Play die Veröffentlichung aller neuen Apps oder Updates, die die unsichere Implementierung der Schnittstelle X509TrustManager enthalten.

Um zu bestätigen, dass Sie die richtigen Änderungen vorgenommen haben, senden Sie die aktualisierte Version Ihrer App an die Developer Console und versuchen Sie es nach fünf Stunden erneut. Wenn die App nicht korrekt aktualisiert wurde, zeigen wir eine Warnung an.

Obwohl diese spezifischen Probleme möglicherweise nicht jede App mit der TrustManager-Implementierung betreffen, ist es am besten, SSL-Zertifikatsvalidierungsfehler nicht zu ignorieren. Apps mit Schwachstellen, die Benutzer dem Risiko einer Kompromittierung aussetzen, können als gefährliche Produkte angesehen werden, die gegen die Inhaltsrichtlinie und Abschnitt 4.4 der Entwicklervertriebsvereinbarung verstoßen.

Benutzer-Avatar
Udara Seneviratne

Ich hatte das gleiche Problem und habe alle oben genannten Vorschläge wie folgt ausprobiert.

  1. Implementieren Sie onReceivedSslError(), indem Sie dem Benutzer die Möglichkeit geben, sich zu entscheiden handler.proceed(); oder handler.cancel(); wenn ein SSL-Fehler aufgetreten ist
  2. Implementieren Sie onReceivedSslError(), um handler.cancel() aufzurufen; wenn ein SSL-Problem aufgetreten ist, ohne die Entscheidung des Benutzers zu berücksichtigen.
  3. Implementieren Sie onReceivedSslError(), um das SSL-Zertifikat zusätzlich zur Überprüfung von error.getPrimaryError() lokal zu überprüfen und dem Benutzer die Entscheidung zu ermöglichen handler.proceed(); oder handler.cancel(); nur wenn das SSL-Zertifikat gültig ist. Wenn nicht, rufen Sie einfach handler.cancel();
  4. Entfernen Sie die Implementierung von onReceivedSslError() und lassen Sie einfach das Android-Standardverhalten eintreten.

Selbst nachdem alle oben genannten Versuche unternommen wurden, sendete Google Play weiterhin dieselbe Benachrichtigungsmail, in der derselbe Fehler und die alte APK-Version erwähnt wurden (obwohl wir bei allen oben genannten Versuchen sowohl den Versionscode als auch den Versionsnamen im Gradle geändert haben).

Wir waren in großen Schwierigkeiten und haben den Google-Support per E-Mail kontaktiert und nachgefragt

“Wir laden die höheren Versionen der APK hoch, aber das Überprüfungsergebnis zeigt den gleichen Fehler, der die alte fehlerhafte APK-Version erwähnt. Was ist der Grund dafür?”

Nach ein paar Tagen hat der Google-Support auf unsere Anfrage wie folgt geantwortet.

Bitte beachten Sie, dass Sie die Version 12 in Ihrem Produktions-Track vollständig ersetzen müssen. Das bedeutet es Sie müssen eine höhere Version vollständig einführen, um Version 12 zu deaktivieren.

Der hervorgehobene Punkt wurde weder in der Spielkonsole noch in einem Forum gefunden oder erwähnt.

Gemäß dieser Richtlinie in der klassischen Google Play-Ansicht haben wir den Produktionstrack überprüft und es gab sowohl fehlerhafte Versionen als auch die neueste Version mit behobenen Fehlern, aber der Rollout-Prozentsatz der Version mit behobenen Fehlern beträgt 20 %. Also auf Vollausbau umgestellt, dann verschwand die Buggy-Version aus der Produktion. Nach mehr als 24 Stunden Überprüfungszeit ist die Version zurückgekehrt.

HINWEIS: Als wir dieses Problem hatten, ist Google gerade auf eine neue UI-Version seiner Spielkonsole umgestiegen und hatte einige Ansichten in der vorherigen UI-Version oder der klassischen Ansicht verpasst. Da wir die neueste Ansicht verwendeten, konnten wir nicht bemerken, was passierte. Was einfach passierte, war, dass Google dieselbe vorherige fehlerhafte Version des APK überprüfte, da die neue nicht vollständig eingeführt wurde.

Benutzer-Avatar
Pabel

Sie können SslError verwenden, um einige Informationen über den Fehler dieses Zertifikats anzuzeigen, und Sie können die Zeichenfolge des Typs error in Ihren Dialog schreiben.

@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.string.notification_error_ssl_date_invalid;
            break;
        case SslError.SSL_EXPIRED:
            mensaje = R.string.notification_error_ssl_expired;
            break;
        case SslError.SSL_IDMISMATCH:
            mensaje = R.string.notification_error_ssl_idmismatch;
            break;
        case SslError.SSL_INVALID:
            mensaje = R.string.notification_error_ssl_invalid;
            break;
        case SslError.SSL_NOTYETVALID:
            mensaje = R.string.notification_error_ssl_not_yet_valid;
            break;
        case SslError.SSL_UNTRUSTED:
            mensaje = R.string.notification_error_ssl_untrusted;
            break;
        default:
            mensaje = R.string.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.string.info, mensaje, R.string.popup_custom_cancelar, R.string.popup_custom_cancelar, listeners);    }

  • Können wir nicht einfach handler.cancel(); innen onReceivedSslError? Wird Google es trotzdem ablehnen?

    – DevAndroid

    14. Februar 2017 um 11:34 Uhr

  • @DevAndroid Google bricht diese Option ab, weil Sie den Benutzer nicht informieren. Bei dieser Filtermethode zeigt der Fehler mehr Informationen an, als weil die Verbindung abgelehnt wurde

    – Pabel

    30. Juni 2017 um 8:48 Uhr

1251400cookie-checkWebView: So vermeiden Sie Sicherheitswarnungen von Google Play bei der Implementierung von onReceivedSslError

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy