在实施onReceivedSslError

时间:2016-03-17 02:43:36

标签: android google-play android-webview android-security sslerrorhandler

我有一个链接将在webview中打开。问题是,在我覆盖onReceivedSslError之前它无法打开:

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

我从Google Play获得安全警报说:

  

安全警报   您的应用程序具有WebViewClient.onReceivedSslError处理程序的不安全实现。具体来说,该实现忽略了所有SSL证书验证错误,使您的应用程序容易受到中间人攻击。攻击者可以使用JavaScript更改受影响的WebView内容,读取传输的数据(例如登录凭据),并在应用程序内执行代码。

     

要正确处理SSL证书验证,只要服务器提供的证书符合您的期望,就更改您的代码以调用SslErrorHandler.proceed(),否则调用SslErrorHandler.cancel()。包含受影响的应用和类别的电子邮件警报已发送到您的开发者帐户地址。

     

请尽快解决此漏洞并增加已升级APK的版本号。有关SSL错误处理程序的更多信息,请参阅开发人员帮助中心中的文档。对于其他技术问题,您可以发布到https://www.stackoverflow.com/questions并使用标签“android-security”和“SslErrorHandler。”如果您使用的是负责此操作的第三方库,请通知第三方并与他们合作解决这个问题。

     

要确认您已正确升级,请将更新后的版本上传到开发者控制台,并在五小时后再回来查看。如果应用尚未正确升级,我们会显示警告。

     

请注意,虽然这些特定问题可能不会影响使用WebView SSL的每个应用,但最好保持所有安全补丁的最新状态。如果应用程序存在漏洞,导致用户面临泄密风险,则可能会被视为违反内容政策和开发者分发协议第4.4节的危险产品。

     

请确保发布的所有应用均符合开发者分发协议和内容政策。如果您有任何疑问或疑虑,请通过Google Play开发者帮助中心与我们的支持小组联系。

如果我删除onReceivedSslError (handler.proceed()),则页面将无法打开。

无论如何,我可以在网页浏览中打开页面并避免安全提醒。

8 个答案:

答案 0 :(得分:97)

  

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

正如电子邮件所说,onReceivedSslError应该处理用户进入具有无效证书的页面,例如通知对话框。你不应该直接进行。

例如,我添加了一个警告对话框,以便用户确认并且Google似乎不再显示警告。

@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的应用程序中处理它。使用警告对话框通知用户是一种简单的方法。

答案 1 :(得分:6)

修复哪些适用于我只是禁用onReceivedSslError中定义的AuthorizationWebViewClient函数。在这种情况下,如果出现SSL错误,将调用handler.cancel。但是,它适用于One Drive SSL证书。在Android 2.3.7,Android 5.1上测试。

答案 2 :(得分:5)

根据Google Security Alert: Unsafe implementation of the interface X509TrustManager,Google Play从2016年7月11日起不会支持X509TrustManager

  

Hello Google Play Hello,

     

此电子邮件末尾列出的您的应用使用不安全的应用   接口X509TrustManager的实现。具体来说,   实现时忽略所有SSL证书验证错误   建立与远程主机的HTTPS连接,从而使您的   应用程序容易受到中间人攻击。攻击者可以阅读   传输数据(如登录凭据)甚至更改数据   在HTTPS连接上传输。如果你有超过20个受影响   您帐户中的应用,请查看开发者控制台的完整信息   列表。

     

要正确处理SSL证书验证,请更改您的代码   自定义X509TrustManager接口的checkServerTrusted方法   无论何时提出CertificateException或IllegalArgumentException   服务器提供的证书不符合您的要求   期望。对于技术问题,您可以发布到Stack Overflow   并使用标签“android-security”和“TrustManager。”

     

请尽快解决此问题并增加   已升级的APK的版本号。从2016年5月17日开始,谷歌   播放将阻止发布任何包含该应用的新应用或更新   接口X509TrustManager的不安全实现。

     

要确认您已进行了正确的更改,请提交更新后的版本   您的应用程序到开发者控制台并在五小时后再回来查看。   如果应用尚未正确升级,我们会显示警告。

     

虽然这些具体问题可能不会影响每个应用程序   TrustManager实现,最好不要忽略SSL证书   验证错误。具有漏洞的应用会使用户面临风险   妥协可能被视为违反的危险产品   内容政策和开发者分发的第4.4节   协议

     

...

答案 3 :(得分:4)

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

我建议将证书嵌入App中,当发生SslError时,检查服务器证书是否与其中一个嵌入证书匹配。

以下是步骤:

  1. 从网站上检索证书。

    • 在Safari上打开网站
    • 点击网站名称附近的挂锁图标
    • 点击显示证书
    • 将证书拖放到文件夹
  2. 请参阅https://www.markbrilman.nl/2012/03/howto-save-a-certificate-via-safari-on-mac/

    1. 将证书(.cer文件)复制到应用的res / raw文件夹中

    2. 在您的代码中,通过调用loadSSLCertificates()

      加载证书
      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. 发生SslError时,请检查服务器证书是否与一个嵌入式证书匹配。请注意,无法直接比较证书,因此我使用SslCertificate.saveState将证书数据放入Bundle,然后我比较所有bundle条目。

      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);
          }
      
      
      });
      

答案 4 :(得分:3)

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

column1,column3 (exactly as a result of SELECT DISTINCT) + two other columns id,column3

答案 5 :(得分:2)

您可以将SslError用于show,有关此认证错误的一些信息,您可以在对话框中写入类型错误的字符串。

@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);    }

答案 6 :(得分:0)

我遇到了同样的问题,并尝试了以下所有上述建议。

  1. 通过给用户机会来实现onReceivedSslError() 决定handler.proceed();或handler.cancel(); SSL错误时 发生
  2. 实施onReceivedSslError()以调用handler.cancel();每当一个 发生SSL问题,没有考虑用户的决定。
  3. 实施onReceivedSslError()以在本地验证SSL证书 除了检查error.getPrimaryError()并向用户提供 决定handler.proceed();或handler.cancel();仅当SSL 证书有效。如果不是,只需调用handler.cancel();
  4. 删除onReceivedSslError()的实现,然后让 发生Android默认行为。

即使尝试了所有上述尝试,Google Play仍在发送相同的通知邮件,提及相同的错误和旧的APK版本(即使在上述所有尝试中,我们都更改了Gradle中的版本代码和版本名称)< / p>

我们遇到了很大的麻烦,并通过邮件联系了Google支持,并询问了

“我们正在上传APK的更高版本,但审核结果 说同样的错误,提到旧的越野车APK版本。那是什么 原因何在?”

几天后,谷歌支持人员回复了我们的要求,如下所示。

请注意,您必须完全替换版本12 生产轨道。这意味着您将必须全面部署更高的 版本以停用版本12

突出显示的点在播放控制台或任何论坛中都没有找到或提及。

根据经典Google Play视图中的指南,我们检查了生产轨迹,发现有错误版本和最新的错误修复版本,但错误修复版本的推出比例为20%。因此,将其完全展开,然后越野车版本从生产轨道上消失了。在超过24小时的审核时间之后,版本又恢复了。

注意:遇到此问题时,Google刚刚移至其游戏机的新UI版本,并且错过了以前的UI版本或经典视图中的某些视图。由于我们使用的是最新视图,因此无法注意到发生了什么。只是发生了什么事,因为新版本尚未完全推出,Google审查了与以前相同的APK版本。

答案 7 :(得分:-1)

在我的情况下:当我们尝试更新apk上传时出现此错误 进入Google Play商店,并获得SSL错误: 然后我使用了以下代码

private class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }

        @Override
        public void onPageFinished(WebView view, String 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.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();
        }
    }