首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >WebView:如何在实现onReceivedSslError时避免来自Google的安全警报

WebView:如何在实现onReceivedSslError时避免来自Google的安全警报
EN

Stack Overflow用户
提问于 2016-03-17 02:43:36
回答 7查看 63.7K关注 0票数 72

我有一个将在WebView中打开的链接。问题是,除非我像这样覆盖onReceivedSslError,否则它无法打开:

代码语言:javascript
复制
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
    handler.proceed();
}

我从Google Play收到了安全警告,上面写着:

安全警报您的应用程序有WebViewClient.onReceivedSslError处理程序的不安全实现。具体来说,该实现忽略了所有SSL证书验证错误,使您的应用程序容易受到中间人攻击.攻击者可以更改受影响的WebView的内容,读取传输的数据(例如登录凭据),并使用JavaScript在应用程序中执行代码。 要正确处理SSL证书验证,请更改代码,以便在服务器提供的证书满足您的期望时调用SslErrorHandler.proceed(),否则调用SslErrorHandler.cancel()。包含受影响应用程序和类(Es)的电子邮件警报已发送到您的开发人员帐户地址。 请尽快解决此漏洞,并增加升级APK的版本号。有关SSL错误处理程序的更多信息,请参见开发人员帮助中心中的文档。对于其他技术问题,您可以在https://www.stackoverflow.com/questions上发布并使用标签“android”和“SslErrorHandler”。如果你使用的是一个第三方图书馆,负责这一点,请通知第三方,并与他们合作,以解决这一问题。 要确认升级是否正确,请将更新后的版本上传到开发人员控制台,并在5个小时后进行检查。如果应用程序没有被正确升级,我们将显示一个警告。 请注意,虽然这些特定的问题可能不会影响到所有使用WebView SSL的应用程序,但最好在所有安全修补程序上保持最新的状态。有漏洞的应用程序会使用户面临妥协的风险,这可能被认为是违反内容策略和开发人员分发协议第4.4节的危险产品。 请确保所有发布的应用程序符合开发人员分发协议和内容策略。如果您有问题或关切,请联系我们的支持团队通过谷歌游戏开发人员帮助中心。

如果我删除了onReceivedSslError (handler.proceed()),那么页面就不会打开。

有什么方法可以在WebView 中打开页面并避免安全警报?

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2016-03-22 06:53:03

要正确处理SSL证书验证,请更改代码,以便在服务器提供的证书满足您的期望时调用SslErrorHandler.proceed(),否则调用SslErrorHandler.cancel()。

正如电子邮件所说的,onReceivedSslError应该处理用户使用无效证书的页面,比如一个通知对话框。你不应该直接去做。

例如,我添加了一个警报对话框,以使用户已经确认,似乎谷歌不再显示警告。

代码语言:javascript
复制
@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();
}

更多关于电子邮件的解释。

具体来说,该实现忽略了所有SSL证书验证错误,使您的应用程序容易受到中间人攻击.

电子邮件说默认实现忽略了一个重要的SSL安全问题。所以我们需要在我们自己的应用程序中处理它,它使用了WebView。使用警报对话框通知用户是一种简单的方法。

票数 131
EN

Stack Overflow用户

发布于 2018-04-05 14:24:50

到目前为止,所提出的解决方案只是绕过了安全检查,所以它们并不安全。

我建议的是将证书嵌入应用程序中,当发生SslError时,检查服务器证书是否与嵌入的证书之一匹配。

下面是几个步骤:

  1. 从网站检索证书。
代码语言:javascript
复制
- Open the site on Safari
- Click on the padlock icon near the website name
- Click on Show Certificate
- Drag and drop the certificate in a folder

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

  1. 将证书(.cer文件)复制到应用程序的res/raw文件夹中
  2. 在代码中,通过调用loadSSLCertificates()加载证书 私有静态最终int[]证书={ R.raw.my_certificate,//您可以放置多个证书};私有ArrayList证书=新的ArrayList<>();私有的loadSSLCertificates() {loadSSLCertificates(){ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");for (int rawId :证书){ InputStream inputStream = getResources().openRawResource(rawId);InputStream certificateInput = new BufferedInputStream(RawId);尝试{证书证书= certificateFactory.generateCertificate(certificateInput);if ( X509Certificate证书实例){ x509Certificate x509Certificate= (X509Certificate)证书;SslCertificate sslCertificate =新SslCertificate( X509Certificate);certificates.add(sslCertificate);}{Log.w(标记,“错误的证书格式:”+ rawId);} catch (CertificateException异常){Log.w(标记,不能读取证书:“+ rawId);}最后{ try { certificateInput.close();inputStream.close();}IOException (IOException e) { e.printStackTrace();} catch (CertificateException e) { e.printStackTrace();}}
  3. 发生SslError时,检查服务器证书是否与一个嵌入证书匹配。请注意,不可能直接比较证书,因此我使用SslCertificate.saveState将证书数据放入包中,然后比较所有的包条目。 WebView.setWebViewClient(新的WebViewClient() {@覆盖公共无效onReceivedSslError(WebView视图,最终SslErrorHandler处理程序,SslError错误)){ //检查嵌入式证书SslCertificate serverCertificate = error.getCertificate();Bundle serverBundle = SslCertificate.saveState(serverCertificate);对于( (TextUtils.equals(serverCertificate.toString(),appCertificate :证书){ if appCertificate appCertificate.toString()){ //第一个快速检查束appBundle = SslCertificate.saveState(appCertificate);Set keySet = appBundle.keySet();布尔匹配= true;for (String key : keySet) { Object serverObj = serverBundle.get(key);对象appObj = appBundle.get( key );if (serverObj instanceof byte[] && appObj instanceof byte[]) { // key“x509-证书”if (!Arrays.equals((byte[]) serverObj,(byte[]) appObj)) { matches = false;break;}}否则if ((serverObj != null) && !serverObj.equals(appObj)) { matches = false;返回;}} handler.cancel();字符串消息= "SSL错误“+ error.getPrimaryError();Log.w(标记,消息);});
票数 10
EN

Stack Overflow用户

发布于 2017-10-18 14:19:33

在向用户显示任何消息之前,我需要检查我们的信任库,所以我这样做了:

代码语言:javascript
复制
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();
    }
}
}
票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36050741

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档