Android Webview在实现OnReceivedSlerRor时避免来自google play的安全警报
我有一个链接将在webview中打开。问题是,在我覆盖OnReceivedSlerror之前,它无法打开,如下所示:Android Webview在实现OnReceivedSlerRor时避免来自google play的安全警报,android,google-play,android-webview,android-security,sslerrorhandler,Android,Google Play,Android Webview,Android Security,Sslerrorhandler,我有一个链接将在webview中打开。问题是,在我覆盖OnReceivedSlerror之前,它无法打开,如下所示: @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); } 我收到google play的安全警报,上面说: 安全警报 您的应用程序具有WebV
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
}
我收到google play的安全警报,上面说:
安全警报
您的应用程序具有WebViewClient.OnReceivedSlerror处理程序的不安全实现。具体而言,该实现会忽略所有SSL证书验证错误,使您的应用容易受到中间人攻击。攻击者可以更改受影响WebView的内容,读取传输的数据(如登录凭据),并使用JavaScript在应用程序内执行代码
若要正确处理SSL证书验证,请更改代码以在服务器提供的证书满足您的期望时调用SslErrorHandler.continue(),否则调用SslErrorHandler.cancel()。包含受影响的应用程序和类的电子邮件警报已发送到您的开发人员帐户地址
请尽快解决此漏洞,并增加已升级APK的版本号。有关SSL错误处理程序的更多信息,请参阅开发人员帮助中心中的文档。对于其他技术问题,您可以发布并使用标签“android security”和“SslErrorHandler”。如果您使用的是负责此问题的第三方库,请通知第三方并与他们合作解决此问题
要确认已正确升级,请将更新的版本上载到开发人员控制台,并在五小时后检查。如果应用程序未正确升级,我们将显示警告
请注意,虽然这些特定问题可能不会影响使用WebView SSL的每个应用程序,但最好在所有安全修补程序上保持最新。存在漏洞的应用程序可能会让用户面临泄露风险,这可能被视为违反内容策略和《开发者发行协议》第4.4节的危险产品
请确保发布的所有应用程序都符合开发者分发协议和内容策略。如果您有疑问或担忧,请通过Google Play开发者帮助中心联系我们的支持团队
若我删除了onReceivedSlerror(handler.procedure()),那个么页面将不会打开
我是否可以在webview中打开页面并避免安全警报。对我有效的修复方法是禁用
在授权WebViewClient
中定义的OnReceivedSlerror
功能。在这种情况下,如果出现SSL错误,将调用处理程序.cancel
。但是,它在单驱动器SSL证书上运行良好。在安卓2.3.7和安卓5.1上测试
要正确处理SSL证书验证,请将代码更改为
每当提交的证书
服务器满足您的期望,并调用
否则为SslErrorHandler.cancel()
正如电子邮件中所说,onReceivedSslError
应该处理用户将要访问的带有无效证书的页面,如通知对话框。你不应该直接进行
例如,我添加了一个警告对话框,让用户确认,谷歌似乎不再显示警告
更多关于电子邮件的解释 具体来说,该实现忽略所有SSL证书验证 错误,使您的应用易受中间人攻击
电子邮件中说,默认实现忽略了一个重要的SSL安全问题。所以我们需要在我们自己使用WebView的应用程序中处理它。使用警报对话框通知用户是一种简单的方法。您可以使用SslError来显示有关此证书错误的一些信息,并且可以在对话框中写入类型错误的字符串
@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); }
据了解,从2016年7月11日起,Google Play将不支持
X509TrustManager
:
你好,谷歌游戏开发者
您在本电子邮件末尾列出的应用程序使用了不安全的
接口X509TrustManager的实现。具体来说
执行时忽略所有SSL证书验证错误
建立到远程主机的HTTPS连接,从而
应用程序易受中间人攻击。攻击者可以阅读
传输数据(如登录凭据),甚至更改数据
通过HTTPS连接传输。如果您有超过20个受影响的
您帐户中的应用程序,请检查开发人员控制台以获取完整信息
名单
要正确处理SSL证书验证,请在
自定义X509TrustManager接口的checkServerTrusted方法
随时引发CertificateException或IllegalArgumentException
服务器提供的证书不符合您的要求
期望。对于技术问题,您可以发布到Stack Overflow
并使用标签“安卓安全”和“信任管理器”
请尽快解决此问题,并增加
已升级的APK的版本号。从2016年5月17日开始,谷歌
Play将阻止发布包含以下内容的任何新应用或更新:
接口X509TrustManager的不安全实现
若要确认所做的更改正确,请提交更新版本
将您的应用程序下载到开发者控制台,并在五小时后返回。
如果应用程序未正确升级,我们将显示警告
虽然这些特定问题可能不会影响到具有
TrustManager实现,最好不要忽略SSL证书
验证错误。具有使用户面临风险的漏洞的应用程序
妥协的结果可能被视为违反
内容策略和开发者发行版第4.4节
同意
在向用户显示任何消息之前,我需要检查我们的信任库,因此我执行了以下操作:
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();
}
}
}
T
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();
}
}
}
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();
}
}
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);
}
});
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();
}
}