Properly handle SSL Certificate Validation when opening Webview.
When you are obtaining this error from Google Play in opening the Webview.
Security alert Your application has an unsafe implementation of the WebViewClient.onReceivedSslError handler. Specifically, the implementation ignores all SSL certificate validation errors, making your app vulnerable to man-in-the-middle attacks. An attacker could change the affected WebView's content, read transmitted data (such as login credentials), and execute code inside the app using JavaScript.
To properly handle SSL certificate validation, change your code to invoke SslErrorHandler.proceed() whenever the certificate presented by the server meets your expectations, and invoke SslErrorHandler.cancel() otherwise. An email alert containing the affected app(s) and class(es) has been sent to your developer account address.
Please address this vulnerability as soon as possible and increment the version number of the upgraded APK. For more information about the SSL error handler, please see our documentation in the Developer Help Center. For other technical questions, you can post to https://www.stackoverflow.com/questions and use the tags “android-security” and “SslErrorHandler.” If you are using a 3rd party library that’s responsible for this, please notify the 3rd party and work with them to address the issue.
To confirm that you've upgraded correctly, upload the updated version to the Developer Console and check back after five hours. If the app hasn't been correctly upgraded, we will display a warning.
Please note, while these specific issues may not affect every app that uses WebView SSL, it's best to stay up to date on all security patches. Apps with vulnerabilities that expose users to risk of compromise may be considered dangerous products in violation of the Content Policy and section 4.4 of the Developer Distribution Agreement.
Please ensure all apps published are compliant with the Developer Distribution Agreement and Content Policy. If you have questions or concerns, please contact our support team through the Google Play Developer Help Center.
Solution is to invoke SslErrorHandler.proceed() whenever the certificate presented by the server meets your expectations, and invoke SslErrorHandler.cancel() otherwise.
@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();
override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) {
CertificateChecker.checkCertificate(applicationContext, handler, error)
}
// SSL REACT WEBVIEW CERTIFICATE CHECKINGval REACTWEBVIEW_CERT_TAG = "REACTWEBVIEW_CERT_TAG"private val certificates: ArrayList<SslCertificate> = ArrayList()
class CertificateChecker {
companion object {
fun checkCertificate(context: Context, handler: SslErrorHandler?, error: SslError?){
Log.e("SSL_ERROR", handler?.obtainMessage().toString())
// Checks Embedded certificates loadSSLCertificates(context)
val serverCertificate = error!!.certificate
val serverBundle = SslCertificate.saveState(serverCertificate)
for (appCertificate in certificates) {
if (TextUtils.equals(
serverCertificate.toString(), appCertificate.toString()
)
) { // First fast check val appBundle = SslCertificate.saveState(appCertificate)
val keySet = appBundle.keySet()
var matches = true for (key in keySet) {
val serverObj = serverBundle[key]
val appObj = appBundle[key]
if (serverObj is ByteArray && appObj is ByteArray) { // key "x509-certificate" if (!Arrays.equals(
serverObj as ByteArray, appObj as ByteArray
)
) {
matches = false break }
} else if (serverObj != null && serverObj != appObj) {
matches = false break }
}
if (matches) {
Log.e(REACTWEBVIEW_CERT_TAG, "Cert Matches... ")
handler!!.proceed()
return }
}
}
handler!!.cancel()
val message = "SSL Error " + error!!.primaryError
Log.w(REACTWEBVIEW_CERT_TAG, message)
}
private fun loadSSLCertificates(context: Context) {
try {
val certificateFactory: CertificateFactory = CertificateFactory.getInstance("X.509")
var inputStream: InputStream = context.resources.openRawResource(R.raw.my_cert)
val certificateInput: InputStream = BufferedInputStream(inputStream)
try {
val certificate: Certificate =
certificateFactory.generateCertificate(certificateInput)
if (certificate is X509Certificate) {
val x509Certificate: X509Certificate = certificate as X509Certificate
val sslCertificate = SslCertificate(x509Certificate)
certificates.add(sslCertificate)
} else {
Log.wtf(REACTWEBVIEW_CERT_TAG, "Wrong Certificate format: ")
}
} catch (exception: CertificateException) {
Log.wtf(REACTWEBVIEW_CERT_TAG, "Cannot read certificate: ")
} finally {
try {
certificateInput.close()
inputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
} catch (e: CertificateException) {
e.printStackTrace()
}
}
}
}
Comments
Post a Comment