As described in my previous article Hacking UWP WebView Part 1. Displaying HTTPS page with invalid certificate in UWP WebView, there is a limited way how to display HTTPS web page with invalid certificate in the UWP WebView. It requires using fake IUriToStreamResolver for making proxy requests to the target web with modified HttpClient that is ignoring certificate errors.

I've even added the possibility to easily load pages with certificate errors to my WebView wishlist of missing features in my article Make WebView great again!.

Luckily, while developing Seznam.cz app for Windows 10 Mobile (Czech only), currently the most advanced browser available on Windows Store, I've found another way how to display web page with invalid domain certificate in a WebView. The problem with this solution is that it looks more like a vulnerability or oversight, because the behavior described below is not documented anywhere.

How it works

First some theory, when navigating in WebView to web site with invalid domain certificate, the NavigationFailed event is raised with WebErrorStatus equal to CertificateIsInvalid. This is the expected behavior for webs with invalid certificates.

To change this behavior we need to create HttpBaseProtocolFilter instance and set it in a way to ignore all ignorable certificate validation errors like this:

HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter ( ) ; filter . IgnorableServerCertificateErrors . Add ( ChainValidationResult . Untrusted ) ; filter . IgnorableServerCertificateErrors . Add ( ChainValidationResult . Expired ) ; filter . IgnorableServerCertificateErrors . Add ( ChainValidationResult . IncompleteChain ) ; filter . IgnorableServerCertificateErrors . Add ( ChainValidationResult . WrongUsage ) ; filter . IgnorableServerCertificateErrors . Add ( ChainValidationResult . InvalidName ) ; filter . IgnorableServerCertificateErrors . Add ( ChainValidationResult . RevocationInformationMissing ) ; filter . IgnorableServerCertificateErrors . Add ( ChainValidationResult . RevocationFailure ) ; HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter(); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.IncompleteChain); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.WrongUsage); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationInformationMissing); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationFailure);

Then we'll create HttpClient instance with this filter and make Head request to the target site that has invalid certificate.

Uri uri = new Uri ( "https://expired.badssl.com/" ) ; HttpClient hc = new HttpClient ( filter ) ; HttpRequestMessage request = new HttpRequestMessage ( HttpMethod . Head , uri ) ; HttpResponseMessage response = await hc . SendRequestAsync ( request ) ; Uri uri = new Uri("https://expired.badssl.com/"); HttpClient hc = new HttpClient(filter); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Head, uri); HttpResponseMessage response = await hc.SendRequestAsync(request);

This request most likely succeeds with HTTP Status Code 200. That's because the HttpBaseProtocolFilter ignores the error we added to the IgnorableServerCertificateErrors list.

Up until now this is all expected behavior of the HttpBaseProtocolFilter and HttpClient class.

What is NOT expected - when you now try to navigate to the same web with invalid certificate inside a WebView now, it works!

Wait, what just happened?

Good question, I have no idea 🙂

Actually I have a hunch that both WebView and HttpBaseProtocolFilter shares the same network stack and somehow when doing request with HttpClient with filter configured to ignore certificate errors, the network stack caches the information about the target domain to ignore certificate errors from now on.

I've done some research to pinpoint the actual behavior and here is the result:

It works like described on Windows 10 Anniversary Update, both Desktop and Mobile, with latest cummulative update (November 2016).

It's necessary to ignore the specific certificate error type for the target domain when doing the HttpClient request with HttpBaseProtocolFilter.

The navigation with invalid certificate in WebView then works only for the specific domain.

Requests to the target domain then work even in new instance of HttpClient with new, empty HttpBaseProtocolFilter.

I was not able to make the target domain un-ignore certificate errors for the rest of app lifetime - all requests to the target web work as long as the app live.

Conclusion

The unforseen consequences in this behavior might be dire - just by making request to target domain with ignoring HttpBaseProtocolFilter in one part of the app, completely different part of the app might start trusting expired or invalid certificates in a WebView. On one hand we discovered, how to easily load page with invalid certificate in the WebView, but on other hand this could compromise the overall security of our app.

There are also some unanswered questions - what happens when using ignoring HttpBaseProtocolFilter in app contracts, calling one app from another?

What happens If I try to use ignoring HttpBaseProtocolFilter in App Extension?

What happens if I create extension for Edge and I use this method on www.microsoft.com, does it mean it could lead to easy way how to MITM any site? Or there are separate network stacks for each app including extensions?

I plan to update this article once I find answers to these questions. For now you can discuss this issue with me on Twitter.