How to check the security state of an XMLHTTPRequest over SSL

  • Revision slug: How_to_check_the_security_state_of_an_XMLHTTPRequest_over_SSL
  • Revision title: How to check the security state of an XMLHTTPRequest over SSL
  • Revision id: 142170
  • Created:
  • Creator: vladimir.dzhuvinov
  • Is current revision? No
  • Comment 2 words added, 3 words removed

Revision Content

Here is a an example Javascript function that prints the security details of an XMLHTTPRequest sent over SSL. The function is passed the channel property of an XMLHTTPRequest to extract the following information:

  • Was the connection secure?
  • Was the used SSL certificate valid and what are its details (owner, expiration, certificate authority, etc.)?

Notes:

  1. This code requires elevated privileges to run; you can only call it from a browser extension or from a XULRunner application.
  2. The channel property becomes available only after the request is sent and the connection was established, that is, on readyState LOADED, INTERACTIVE or COMPLETED.
  3. By setting the mozBackgroundRequest property of the request object and modifying the example appropriately, you can create your own alert dialog to handle SSL exceptions in your Firefox extension or XULRunner application.
function dumpSecurityInfo(channel) {

	try {
		const Ci = Components.interfaces;
		
		// Do we have a valid channel argument?
		if (! channel instanceof  Ci.nsIChannel) {
			dump("No channel available\n");
			return;
		}
		
		var secInfo = channel.securityInfo;
		
		
		// Print general connection security state
		dump("Security Info:\n");
		
		if (secInfo instanceof Ci.nsITransportSecurityInfo) {
			
			secInfo.QueryInterface(Ci.nsITransportSecurityInfo);
			
			dump("\tSecurity state: ");
			
			// Check security state flags
			if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_SECURE) == Ci.nsIWebProgressListener.STATE_IS_SECURE)
				dump("secure\n");
			
			else if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_INSECURE) == Ci.nsIWebProgressListener.STATE_IS_INSECURE)
				dump("insecure\n");
				
			else if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_BROKEN) == Ci.nsIWebProgressListener.STATE_IS_BROKEN)
				dump("unknown\n");
			
			dump("\tSecurity description: " + secInfo.shortSecurityDescription + "\n");
			dump("\tSecurity error message: " + secInfo.errorMessage + "\n");
		}
		else {
			
			dump("\tNo security info available for this channel\n");
		}
		
		// Print SSL certificate details
		if (secInfo instanceof Ci.nsISSLStatusProvider) {
			
			var cert = secInfo.QueryInterface(Ci.nsISSLStatusProvider).
			SSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
			
			dump("\nCertificate Status:\n");
			
			var verificationResult = cert.verifyForUsage(Ci.nsIX509Cert.CERT_USAGE_SSLServer);
			dump("\tVerification: ");
			
			switch (verificationResult) {
				case Ci.nsIX509Cert.VERIFIED_OK:
					dump("OK");
					break;
				case Ci.nsIX509Cert.NOT_VERIFIED_UNKNOWN:
					dump("not verfied/unknown");
					break;
				case Ci.nsIX509Cert.CERT_REVOKED:
					dump("revoked");
					break;
				case Ci.nsIX509Cert.CERT_EXPIRED:
					dump("expired");
					break;
				case Ci.nsIX509Cert.CERT_NOT_TRUSTED:
					dump("not trusted");
					break;
				case Ci.nsIX509Cert.ISSUER_NOT_TRUSTED:
					dump("issuer not trusted");
					break;
				case Ci.nsIX509Cert.ISSUER_UNKNOWN:
					dump("issuer unknown");
					break;
				case Ci.nsIX509Cert.INVALID_CA:
					dump("invalid CA");
					break;
				default:
					dump("unexpected failure");
					break;
			}
			dump("\n");
			
			dump("\tCommon name (CN) = " + cert.commonName + "\n");
			dump("\tOrganisation = " + cert.organization + "\n");
			dump("\tIssuer = " + cert.issuerOrganization + "\n");
			dump("\tSHA1 fingerprint = " + cert.sha1Fingerprint + "\n");
			
			var validity = cert.validity.QueryInterface(Ci.nsIX509CertValidity);
			dump("\tValid from " + validity.notBeforeGMT + "\n");
			dump("\tValid until " + validity.notAfterGMT + "\n");
		}
	} catch(err) {
		alert(err);
	}
}

Example usage of the dumpSecurityInfo() function:

var httpRequest = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
	
// Disable alert popups on SSL error
httpRequest.mozBackgroundRequest = true;
	
httpRequest.open("GET", "https://developer.mozilla.org/", true); 
	
httpRequest.onreadystatechange = function (aEvt) {  
	if (httpRequest.readyState == 4) {
			
		// Print security state of request
		dumpSecurityInfo(httpRequest.channel);
	}
};

httpRequest.send(null);

which produced the following output in my console:

Security Info:
	Security state: secure
	Security description: Authenticated by Equifax
	Security error message: null

Certificate Status:
	Verification: OK
	Common name (CN) = *.mozilla.org
	Organisation = Mozilla Corporation
	Issuer = Equifax
	SHA1 fingerprint = 45:8A:8E:66:86:0D:6C:F9:EE:09:35:0B:DE:00:C0:70:C5:72:2B:FD
	Valid from 12/10/2007 18:02:33
	Valid until 12/10/2009 18:02:33

 

And here is the output of making an HTTPS request to a localhost server that uses a self-signed certificate. Note that the security state has become "insecure" now:

Security Info:
	Security state: insecure
	Security description: null
	Security error message: null

Certificate Status:
	Verification: not verfied/unknown
	Common name (CN) = localhost
	Organisation = 
	Issuer = localhost
	SHA1 fingerprint = AF:66:89:10:29:84:EC:35:C4:CB:EC:1A:55:77:73:6D:57:0F:1A:BC
	Valid from 11/17/2008 13:27:44
	Valid until 11/15/2018 13:27:44

 

Revision Source

<p>Here is a an example Javascript function that prints the security details of an <a class="internal" href="/en/XMLHttpRequest" title="en/XMLHttpRequest">XMLHTTPRequest</a> sent over SSL. The function is passed the channel property of an XMLHTTPRequest to extract the following information:</p>
<ul> <li>Was the connection secure?</li> <li>Was the used SSL certificate valid and what are its details (owner, expiration, certificate authority, etc.)?</li>
</ul>
<p>Notes:</p>
<ol> <li>This code requires elevated privileges to run; you can only call it from a browser extension or from a XULRunner application.</li> <li>The channel property becomes available only after the request is sent and the connection was established, that is, on readyState <code>LOADED, </code><code>INTERACTIVE or COMPLETED.<br> </code></li> <li>By setting the mozBackgroundRequest property of the request object and modifying the example appropriately, you can create your own alert dialog to handle SSL exceptions in your Firefox extension or XULRunner application.</li>
</ol>
<pre>function dumpSecurityInfo(channel) {

	try {
		const Ci = Components.interfaces;
		
		// Do we have a valid channel argument?
		if (! channel instanceof  Ci.nsIChannel) {
			dump("No channel available\n");
			return;
		}
		
		var secInfo = channel.securityInfo;
		
		
		// Print general connection security state
		dump("Security Info:\n");
		
		if (secInfo instanceof Ci.nsITransportSecurityInfo) {
			
			secInfo.QueryInterface(Ci.nsITransportSecurityInfo);
			
			dump("\tSecurity state: ");
			
			// Check security state flags
			if ((secInfo.securityState &amp; Ci.nsIWebProgressListener.STATE_IS_SECURE) == Ci.nsIWebProgressListener.STATE_IS_SECURE)
				dump("secure\n");
			
			else if ((secInfo.securityState &amp; Ci.nsIWebProgressListener.STATE_IS_INSECURE) == Ci.nsIWebProgressListener.STATE_IS_INSECURE)
				dump("insecure\n");
				
			else if ((secInfo.securityState &amp; Ci.nsIWebProgressListener.STATE_IS_BROKEN) == Ci.nsIWebProgressListener.STATE_IS_BROKEN)
				dump("unknown\n");
			
			dump("\tSecurity description: " + secInfo.shortSecurityDescription + "\n");
			dump("\tSecurity error message: " + secInfo.errorMessage + "\n");
		}
		else {
			
			dump("\tNo security info available for this channel\n");
		}
		
		// Print SSL certificate details
		if (secInfo instanceof Ci.nsISSLStatusProvider) {
			
			var cert = secInfo.QueryInterface(Ci.nsISSLStatusProvider).
			SSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
			
			dump("\nCertificate Status:\n");
			
			var verificationResult = cert.verifyForUsage(Ci.nsIX509Cert.CERT_USAGE_SSLServer);
			dump("\tVerification: ");
			
			switch (verificationResult) {
				case Ci.nsIX509Cert.VERIFIED_OK:
					dump("OK");
					break;
				case Ci.nsIX509Cert.NOT_VERIFIED_UNKNOWN:
					dump("not verfied/unknown");
					break;
				case Ci.nsIX509Cert.CERT_REVOKED:
					dump("revoked");
					break;
				case Ci.nsIX509Cert.CERT_EXPIRED:
					dump("expired");
					break;
				case Ci.nsIX509Cert.CERT_NOT_TRUSTED:
					dump("not trusted");
					break;
				case Ci.nsIX509Cert.ISSUER_NOT_TRUSTED:
					dump("issuer not trusted");
					break;
				case Ci.nsIX509Cert.ISSUER_UNKNOWN:
					dump("issuer unknown");
					break;
				case Ci.nsIX509Cert.INVALID_CA:
					dump("invalid CA");
					break;
				default:
					dump("unexpected failure");
					break;
			}
			dump("\n");
			
			dump("\tCommon name (CN) = " + cert.commonName + "\n");
			dump("\tOrganisation = " + cert.organization + "\n");
			dump("\tIssuer = " + cert.issuerOrganization + "\n");
			dump("\tSHA1 fingerprint = " + cert.sha1Fingerprint + "\n");
			
			var validity = cert.validity.QueryInterface(Ci.nsIX509CertValidity);
			dump("\tValid from " + validity.notBeforeGMT + "\n");
			dump("\tValid until " + validity.notAfterGMT + "\n");
		}
	} catch(err) {
		alert(err);
	}
}
</pre>
<p>Example usage of the dumpSecurityInfo() function:</p>
<pre>var httpRequest = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
	
// Disable alert popups on SSL error
httpRequest.mozBackgroundRequest = true;
	
httpRequest.open("GET", "https://developer.mozilla.org/", true); 
	
httpRequest.onreadystatechange = function (aEvt) {  
	if (httpRequest.readyState == 4) {
			
		// Print security state of request
		dumpSecurityInfo(httpRequest.channel);
	}
};

httpRequest.send(null);
</pre>
<p>which produced the following output in my console:</p>
<pre>Security Info:
	Security state: secure
	Security description: Authenticated by Equifax
	Security error message: null

Certificate Status:
	Verification: OK
	Common name (CN) = *.mozilla.org
	Organisation = Mozilla Corporation
	Issuer = Equifax
	SHA1 fingerprint = 45:8A:8E:66:86:0D:6C:F9:EE:09:35:0B:DE:00:C0:70:C5:72:2B:FD
	Valid from 12/10/2007 18:02:33
	Valid until 12/10/2009 18:02:33
</pre>
<p> </p>
<p>And here is the output of making an HTTPS request to a localhost server that uses a self-signed certificate. Note that the security state has become "insecure" now:</p>
<pre>Security Info:
	Security state: insecure
	Security description: null
	Security error message: null

Certificate Status:
	Verification: not verfied/unknown
	Common name (CN) = localhost
	Organisation = 
	Issuer = localhost
	SHA1 fingerprint = AF:66:89:10:29:84:EC:35:C4:CB:EC:1A:55:77:73:6D:57:0F:1A:BC
	Valid from 11/17/2008 13:27:44
	Valid until 11/15/2018 13:27:44
</pre>
<p> </p>
Revert to this revision