WebView在执行onReceivedSslError时避免来自Google play的安全警报
我有一个链接,将在web视图中打开。 问题是它不能打开,直到我重写onReceivedSslError像这样:
@Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); }
我从谷歌播放安全警报说:
安全警报您的应用程序具有WebViewClient.onReceivedSslError处理程序的不安全实现。 具体来说,该实施忽略了所有的SSL证书validation错误,使您的应用程序容易受到中间人攻击。 攻击者可以更改受影响的WebView的内容,读取传输的数据(如login凭据),并使用JavaScript在应用程序内部执行代码。
为了正确处理SSL证书validation,只要服务器提供的证书满足您的期望,请更改您的代码以调用SslErrorHandler.proceed(),否则请调用SslErrorHandler.cancel()。 包含受影响的应用程序和类的电子邮件警报已发送到您的开发者帐户地址。
请尽快解决此漏洞,并增加升级APK的版本号。 有关SSLerror handling程序的更多信息,请参阅开发人员帮助中心的文档。 对于其他技术问题,您可以发布到https://www.stackoverflow.com/questions并使用标签“android-security”和“SslErrorHandler”。如果您使用的是负责此的第三方库,请通知第三方,并与他们一起解决这个问题。
要确认您已正确升级,请将更新后的版本上传到开发者控制台,并在五个小时后再回来查看。 如果应用程序未正确升级,我们将显示警告。
请注意,虽然这些特定问题可能不会影响使用WebView SSL的每个应用,但最好在所有安全补丁上保持最新状态。 存在漏洞的应用程序可能会被视为危险产品,违反了内容政策和开发者分发协议第4.4节的规定。
请确保所有已发布的应用都符合开发者分发协议和内容政策。 如果您有任何问题或疑虑,请通过Google Play开发人员帮助中心与我们的支持小组联系。
如果我删除onReceivedSslError (handler.proceed())
,那么页面将不会打开。
无论如何,我可以打开页面的web视图,并避免安全警报。
为了正确处理SSL证书validation,只要服务器提供的证书满足您的期望,请更改您的代码以调用SslErrorHandler.proceed(),否则请调用SslErrorHandler.cancel()。
正如电子邮件所说, onReceivedSslError
应该处理用户正在去一个页面与无效的证书,如通知对话框。 你不应该直接进行。
例如,我添加了一个警告对话框,使用户已经确认,似乎谷歌不再显示警告。
@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证书validation错误,使您的应用程序容易受到中间人攻击。
该电子邮件说默认工具忽略了一个重要的SSL安全问题。 所以我们需要在使用WebView的应用程序中处理它。 通知用户一个警告对话框是一个简单的方法。
修正这对我的作品只是禁用AuthorizationWebViewClient
定义的onReceivedSslError
函数。 在这种情况下, handler.cancel
将在SSL错误的情况下被调用。 但是,它适用于One Drive SSL证书。 testingAndroid 2.3.7,Android 5.1。
根据谷歌安全警报:X509TrustManager界面不安全的实现 ,Google Play将从2016年7月11日起不支持X509TrustManager
:
Google Play开发者,您好!
本电子邮件末尾列出的您的应用程序使用接口X509TrustManager的不安全实现。 具体而言,实施会在与远程主机build立HTTPS连接时忽略所有SSL证书validation错误,从而使您的应用容易受到中间人攻击。 攻击者可以读取传输的数据(如login凭证),甚至可以更改在HTTPS连接上传输的数据。 如果您的帐户中有超过20个受影响的应用,请查看Developer Console以查看完整列表。
要正确处理SSL证书validation,请在自定义X509TrustManager接口的checkServerTrusted方法中更改您的代码,以便在服务器提供的证书不符合您的期望时引发CertificateException或IllegalArgumentException。 对于技术问题,您可以发布到Stack Overflow并使用标签“android-security”和“TrustManager”。
请尽快解决此问题,并增加升级APK的版本号。 从2016年5月17日起,Google Play将阻止发布任何包含不安全的X509TrustManager界面的新应用或更新。
要确认您做出了正确的更改,请将更新后的应用版本提交到开发者控制台,并在五小时后再回来查看。 如果应用程序未正确升级,我们将显示警告。
尽pipe这些特定问题可能不会影响TrustManager实施的每个应用程序,但最好不要忽略SSL证书validation错误。 存在漏洞的应用程序可能会被视为危险产品,违反了内容政策和开发者分发协议第4.4节的规定。
…
你可以使用SslError来显示这个authentication错误的一些信息,你可以在对话框中写入types错误的string。
@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); }
在向用户显示任何消息之前,我需要检查我们的信任库,所以我这样做了:
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(); } } }