How to check the security state of an XMLHTTPRequest over SSL - Web APIs 编辑
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:
- This code requires elevated privileges to run; you can only call it from a browser extension or from a XULRunner application.
- The channel property becomes available only after the request is sent and the connection was established, that is, on readyState
LOADED,
INTERACTIVE
orCOMPLETED.
- 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.
// Adapted from the patch for mozTCPSocket error reporting (bug 861196).
const {Cc,Ci} = require("chrome");
function createTCPErrorFromFailedXHR(xhr) {
let status = xhr.channel.QueryInterface(Ci.nsIRequest).status;
let errType;
if ((status & 0xff0000) === 0x5a0000) { // Security module
const nsINSSErrorsService = Ci.nsINSSErrorsService;
let nssErrorsService = Cc['@mozilla.org/nss_errors_service;1'].getService(nsINSSErrorsService);
let errorClass;
// getErrorClass will throw a generic NS_ERROR_FAILURE if the error code is
// somehow not in the set of covered errors.
try {
errorClass = nssErrorsService.getErrorClass(status);
} catch (ex) {
//catching security protocol exception
errorClass = 'SecurityProtocol';
}
if (errorClass == nsINSSErrorsService.ERROR_CLASS_BAD_CERT) {
errType = 'SecurityCertificate';
} else {
errType = 'SecurityProtocol';
}
// NSS_SEC errors (happen below the base value because of negative vals)
if ((status & 0xffff) < Math.abs(nsINSSErrorsService.NSS_SEC_ERROR_BASE)) {
// The bases are actually negative, so in our positive numeric space, we
// need to subtract the base off our value.
let nssErr = Math.abs(nsINSSErrorsService.NSS_SEC_ERROR_BASE) - (status & 0xffff);
switch (nssErr) {
case 11: // SEC_ERROR_EXPIRED_CERTIFICATE, sec(11)
errName = 'SecurityExpiredCertificateError';
break;
case 12: // SEC_ERROR_REVOKED_CERTIFICATE, sec(12)
errName = 'SecurityRevokedCertificateError';
break;
// per bsmith, we will be unable to tell these errors apart very soon,
// so it makes sense to just folder them all together already.
case 13: // SEC_ERROR_UNKNOWN_ISSUER, sec(13)
case 20: // SEC_ERROR_UNTRUSTED_ISSUER, sec(20)
case 21: // SEC_ERROR_UNTRUSTED_CERT, sec(21)
case 36: // SEC_ERROR_CA_CERT_INVALID, sec(36)
errName = 'SecurityUntrustedCertificateIssuerError';
break;
case 90: // SEC_ERROR_INADEQUATE_KEY_USAGE, sec(90)
errName = 'SecurityInadequateKeyUsageError';
break;
case 176: // SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, sec(176)
errName = 'SecurityCertificateSignatureAlgorithmDisabledError';
break;
default:
errName = 'SecurityError';
break;
}
} else {
// Calculating the difference
let sslErr = Math.abs(nsINSSErrorsService.NSS_SSL_ERROR_BASE) - (status & 0xffff);
switch (sslErr) {
case 3: // SSL_ERROR_NO_CERTIFICATE, ssl(3)
errName = 'SecurityNoCertificateError';
break;
case 4: // SSL_ERROR_BAD_CERTIFICATE, ssl(4)
errName = 'SecurityBadCertificateError';
break;
case 8: // SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE, ssl(8)
errName = 'SecurityUnsupportedCertificateTypeError';
break;
case 9: // SSL_ERROR_UNSUPPORTED_VERSION, ssl(9)
errName = 'SecurityUnsupportedTLSVersionError';
break;
case 12: // SSL_ERROR_BAD_CERT_DOMAIN, ssl(12)
errName = 'SecurityCertificateDomainMismatchError';
break;
default:
errName = 'SecurityError';
break;
}
}
} else {
errType = 'Network';
switch (status) {
// connect to host:port failed
case 0x804B000C: // NS_ERROR_CONNECTION_REFUSED, network(13)
errName = 'ConnectionRefusedError';
break;
// network timeout error
case 0x804B000E: // NS_ERROR_NET_TIMEOUT, network(14)
errName = 'NetworkTimeoutError';
break;
// hostname lookup failed
case 0x804B001E: // NS_ERROR_UNKNOWN_HOST, network(30)
errName = 'DomainNotFoundError';
break;
case 0x804B0047: // NS_ERROR_NET_INTERRUPT, network(71)
errName = 'NetworkInterruptError';
break;
default:
errName = 'NetworkError';
break;
}
}
// XXX we have no TCPError implementation right now because it's really hard to
// do on b2g18. On mozilla-central we want a proper TCPError that ideally
// sub-classes DOMError. Bug 867872 has been filed to implement this and
// contains a documented TCPError.webidl that maps all the error codes we use in
// this file to slightly more readable explanations.
let error = Cc["@mozilla.org/dom-error;1"].createInstance(Ci.nsIDOMDOMError);
error.wrappedJSObject.init(errName);
return error;
// XXX: errType goes unused
}
function dumpSecurityInfo(xhr, error) {
let channel = xhr.channel;
try {
dump("Connection status:\n");
if (!error) {
dump("\tsucceeded\n");
} else {
dump("\tfailed: " + error.name + "\n");
}
let secInfo = channel.securityInfo;
// Print general connection security state
dump("Security Information:\n");
if (secInfo instanceof Ci.nsITransportSecurityInfo) {
secInfo.QueryInterface(Ci.nsITransportSecurityInfo);
dump("\tSecurity state of connection: ");
// Check security state flags
if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_SECURE)
== Ci.nsIWebProgressListener.STATE_IS_SECURE) {
dump("secure connection\n");
} else if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_INSECURE)
== Ci.nsIWebProgressListener.STATE_IS_INSECURE) {
dump("insecure connection\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("\tCommon name (CN) = " + cert.commonName + "\n");
dump("\tIssuer = " + cert.issuerOrganization + "\n");
dump("\tOrganisation = " + cert.organization + "\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);
}
}
function test(url) {
var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
req.open('GET', url, true);
req.addEventListener("error",
function(e) {
var error = createTCPErrorFromFailedXHR(req);
dumpSecurityInfo(req, error);
},
false);
req.onload = function(e) {
dumpSecurityInfo(req);
};
req.send();
}
Then
test("https://addons.mozilla.org");
produced the following output in my console:
Connection status: succeeded Security Info: Security state: secure Common name (CN) = addons.mozilla.org Organisation = Mozilla Corporation Issuer = VeriSign, Inc. SHA1 fingerprint = F4:99:64:18:6B:7D:C8:FA:C0:0C:2E:A9:61:77:28:67:13:C4:97:7B Valid from 7/14/2011 0:00:00 AM Valid until 8/20/2013 23:59:59 PM
And here is the output of making an HTTPS request to a server that uses an expired certificate:
test("https://www.appliancetherapy.com/");
Note that the security state has become "insecure" now and there is an error name reported:
Connection status: failed: SecurityExpiredCertificateError Security Info: Security state: insecure Common name (CN) = www.appliancetherapy.com Organisation = Appliance Therapy Group (SELANE PRODUCTS, INC) Issuer = VeriSign, Inc. SHA1 fingerprint = F1:8C:38:96:0A:30:63:16:47:FA:6E:CD:7D:58:CC:AB:82:FB:A9:D0 Valid from 9/1/2010 0:00:00 AM Valid until 9/1/2012 23:59:59 PM
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论