android - que - Webview evita la alerta de seguridad de Google Play al implementar onReceivedSslError
play store descargar (7)
Tengo un enlace que se abrirá en webview. El problema es que no puede abrirse hasta que anule onReceivedSslError de esta manera:
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
}
Recibo una alerta de seguridad de Google Play que dice:
Alerta de seguridad Su aplicación tiene una implementación insegura del controlador WebViewClient.onReceivedSslError. Específicamente, la implementación ignora todos los errores de validación de certificados SSL, lo que hace que su aplicación sea vulnerable a los ataques de intermediarios. Un atacante podría cambiar el contenido del WebView afectado, leer los datos transmitidos (como las credenciales de inicio de sesión) y ejecutar código dentro de la aplicación usando JavaScript.
Para manejar correctamente la validación del certificado SSL, cambie su código para invocar SslErrorHandler.proceed () siempre que el certificado presentado por el servidor cumpla con sus expectativas e invoque SslErrorHandler.cancel () de lo contrario. Se ha enviado una alerta por correo electrónico que contiene la (s) aplicación (es) y clase (s) afectadas (s) a la dirección de su cuenta de desarrollador.
Aborde esta vulnerabilidad lo antes posible e incremente el número de versión del APK actualizado. Para obtener más información sobre el controlador de errores SSL, consulte nuestra documentación en el Centro de ayuda para desarrolladores. Para otras preguntas técnicas, puede publicar en https://www.stackoverflow.com/questions y usar las etiquetas "android-security" y "SslErrorHandler". Si está utilizando una biblioteca de terceros responsable de esto, notifique al Terceros y trabajar con ellos para abordar el problema.
Para confirmar que ha actualizado correctamente, cargue la versión actualizada en la Consola de desarrollador y vuelva a consultar después de cinco horas. Si la aplicación no se ha actualizado correctamente, mostraremos una advertencia.
Tenga en cuenta que, si bien estos problemas específicos pueden no afectar a todas las aplicaciones que usan SSL de WebView, es mejor mantenerse actualizado en todos los parches de seguridad. Las aplicaciones con vulnerabilidades que exponen a los usuarios al riesgo de compromiso pueden considerarse productos peligrosos en violación de la Política de contenido y la sección 4.4 del Acuerdo de distribución para desarrolladores.
Asegúrese de que todas las aplicaciones publicadas cumplan con el Acuerdo de distribución para desarrolladores y la Política de contenido. Si tiene preguntas o inquietudes, comuníquese con nuestro equipo de soporte a través del Centro de ayuda para desarrolladores de Google Play.
Si
onReceivedSslError (handler.proceed())
, la página no se abrirá.
¿Hay alguna forma de abrir la página en webview y evitar alertas de seguridad?
Para manejar correctamente la validación del certificado SSL, cambie su código para invocar SslErrorHandler.proceed () siempre que el certificado presentado por el servidor cumpla con sus expectativas e invoque SslErrorHandler.cancel () de lo contrario.
Como decía el correo electrónico,
onReceivedSslError
debería manejar que el usuario vaya a una página con un certificado no válido, como un diálogo de notificación.
No debe proceder directamente.
Por ejemplo, agrego un cuadro de diálogo de alerta para que el usuario haya confirmado y parece que Google ya no muestra advertencia.
@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();
}
Más explica sobre el correo electrónico.
Específicamente, la implementación ignora todos los errores de validación de certificados SSL, lo que hace que su aplicación sea vulnerable a los ataques de intermediarios.
El correo electrónico dice que el implemento predeterminado ignoró un importante problema de seguridad SSL. Por lo tanto, debemos manejarlo en nuestra propia aplicación que usó WebView. Notificar al usuario con un cuadro de diálogo de alerta es una forma simple.
De acuerdo con
Google Security Alert: implementación insegura de la interfaz X509TrustManager
, Google Play no admitirá
X509TrustManager
partir del 11 de julio de 2016:
Hola, desarrollador de Google Play:
Sus aplicaciones enumeradas al final de este correo electrónico utilizan una implementación insegura de la interfaz X509TrustManager. Específicamente, la implementación ignora todos los errores de validación de certificados SSL al establecer una conexión HTTPS a un host remoto, lo que hace que su aplicación sea vulnerable a los ataques de intermediarios. Un atacante podría leer los datos transmitidos (como las credenciales de inicio de sesión) e incluso cambiar los datos transmitidos en la conexión HTTPS. Si tiene más de 20 aplicaciones afectadas en su cuenta, consulte la Consola de desarrollador para obtener una lista completa.
Para manejar correctamente la validación del certificado SSL, cambie su código en el método checkServerTrusted de su interfaz X509TrustManager personalizada para generar CertificateException o IllegalArgumentException siempre que el certificado presentado por el servidor no cumpla con sus expectativas. Para preguntas técnicas, puede publicar en y usar las etiquetas "android-security" y "TrustManager".
Solucione este problema lo antes posible e incremente el número de versión del APK actualizado. A partir del 17 de mayo de 2016, Google Play bloqueará la publicación de cualquier aplicación nueva o actualización que contenga la implementación insegura de la interfaz X509TrustManager.
Para confirmar que ha realizado los cambios correctos, envíe la versión actualizada de su aplicación a la Consola del desarrollador y vuelva a consultar después de cinco horas. Si la aplicación no se ha actualizado correctamente, mostraremos una advertencia.
Si bien estos problemas específicos pueden no afectar a todas las aplicaciones con la implementación de TrustManager, es mejor no ignorar los errores de validación de certificados SSL. Las aplicaciones con vulnerabilidades que exponen a los usuarios al riesgo de compromiso pueden considerarse productos peligrosos en violación de la Política de contenido y la sección 4.4 del Acuerdo de distribución para desarrolladores.
...
En mi situación: este error se produjo cuando intentamos actualizar el apk cargado en la tienda de Google Play y obtener un error de SSL: luego he usado el siguiente código
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();
}
}
La solución que funciona para mí es simplemente deshabilitar la función
onReceivedSslError
definida en
AuthorizationWebViewClient
.
En este caso, se
handler.cancel
a
handler.cancel
en caso de error SSL.
Sin embargo, funciona bien con los certificados SSL de One Drive.
Probado en Android 2.3.7, Android 5.1.
Las soluciones propuestas hasta ahora solo pasan por alto el control de seguridad, por lo que no son seguras.
Lo que sugiero es incrustar los certificados en la aplicación, y cuando ocurra un SslError, verifique que el certificado del servidor coincida con uno de los certificados incrustados.
Así que aquí están los pasos:
-
Recupere el certificado del sitio web.
- Abre el sitio en Safari
- Haga clic en el icono del candado cerca del nombre del sitio web
- Haga clic en Mostrar certificado
- Arrastra y suelta el certificado en una carpeta
ver https://www.markbrilman.nl/2012/03/howto-save-a-certificate-via-safari-on-mac/
-
Copie el certificado (archivo .cer) en la carpeta res / raw de su aplicación
-
En su código, cargue los certificados llamando a 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(); } }
-
Cuando se produce un SslError, verifique que el certificado del servidor coincida con un certificado incrustado. Tenga en cuenta que no es posible comparar directamente los certificados, por lo que utilizo SslCertificate.saveState para colocar los datos del certificado en un paquete, y luego comparo todas las entradas del paquete.
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); } });
Necesitaba verificar nuestro almacén de confianza antes de mostrar cualquier mensaje al usuario, así que hice esto:
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();
}
}
}
Puede usar SslError para mostrar, alguna información sobre el error de este certificado, y puede escribir en su cuadro de diálogo la cadena del error de tipo.
@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); }