Untrusted SSL Certificates
This documentation previously located on the wiki
Introduction
This page details how WebDriver is able to accept untrusted SSL certificates, allowing users to test trusted sites in a testing environment, where valid certificates usually do not exist. This feature is turned on by default for all supported browsers (Currently Firefox).
Firefox
Outline of solution
Firefox has an interface for overriding invalid certificates, called nsICertOverrideService. Implement this interface as a proxy to the original service - unless untrusted certificates are allowed. In that case, when asked about a certificate (a call to hasMatchingOverride for an invalid certificate) - indicate it’s trusted.
Implementation details
Implementing the idea is mostly straightforward - badCertListener.js is a stand-alone module, that, when loaded, registers a factory for returning an instance of the service. The interesting function is hasMatchingOverride:
WdCertOverrideService.prototype.hasMatchingOverride = function(
aHostName, aPort, aCert, aOverrideBits, aIsTemporary)
The aOverrideBits and aIsTemporary are output arguments. This is where things get a bit tricky: There are three possible override bits:
ERROR_UNTRUSTED: 1,
ERROR_MISMATCH: 2,
ERROR_TIME: 4
It’s impossible to just set them all, since Firefox expects a perfect match between the offences generated by the certificate and the function’s return value: (security/manager/ssl/src/SSLServerCertVerification.cpp:302):
if (overrideService)
{
PRBool haveOverride;
PRBool isTemporaryOverride; // we don't care
nsrv = overrideService->HasMatchingOverride(hostString, port,
ix509,
&overrideBits,
&isTemporaryOverride,
&haveOverride);
if (NS_SUCCEEDED(nsrv) && haveOverride)
{
// remove the errors that are already overriden
remaining_display_errors -= overrideBits;
}
}
if (!remaining_display_errors) {
// all errors are covered by override rules, so let's accept the cert
return SECSuccess;
}
The exact mapping of violation to error code can be easily seen at security/manager/pki/resources/content/exceptionDialog.js (in Firefox source):
var flags = 0;
if(gSSLStatus.isUntrusted)
flags |= overrideService.ERROR_UNTRUSTED;
if(gSSLStatus.isDomainMismatch)
flags |= overrideService.ERROR_MISMATCH;
if(gSSLStatus.isNotValidAtThisTime)
flags |= overrideService.ERROR_TIME;
The SSL status can be obtained from "@mozilla.org/security/recentbadcerts;1"
usually - However, the certificate (and its status) are added to this service only after the call to hasMatchingOverride
, so there is no easy way to find out the certificate’s SSLStatus. Instead, the checks have to be executed manually.
Two checks are carried out:
- Calling
nsIX509Cert.verifyForUsage
- Comparing hostname against
nsIX509Cert.commonName
. If those are not equal,ERROR_MISMATCH
is set.
The second check indicates whether ERROR_MISMATCH
should be set.
The first check should indicate whether ERROR_UNTRUSTED
and ERROR_TIME
should be set. Unfortunately, it does not work reliably when the certificate expired and is from an untrusted issuer. When the certificate has expired, the return code would be CERT_EXPIRED
even if it is also untrusted. For this reason, the FirefoxDriver assumes that certificates will be untrusted - it always sets the ERROR_UNTRUSTED
bit - the other two will be set only if the conditions for them are met.
This could pose a problem for someone testing a site with a valid certificate that does not match the host name it’s served from (e.g. test environment serving production certificates). An additional feature for FirefoxProfile
was added: FirefoxProfile.setAssumeUntrustedCertificateIssuer
. Calling this function with false
will turn the ERROR_UNTRUSTED
bit off and allow a user to work in such situation.
HTMLUnit
Not tested yet.
IE
Not implemented yet.
Chrome
Not implemented yet.