问题描述
我试图开发一个需要访问当前加载页面的SSL证书信息的Firefox扩展插件。一旦我有这个信息,我打算根据SSL信息修改页面的内容。虽然,在我到达那里之前,我首先需要获取SSL信息。概述使得单独的XMLHTTPRequest获得安全证书。我宁愿不这样做,如果我能避免它,因为它提出了一个安全问题。例如,恶意站点/中间人可以在页面的第一个请求(浏览器将会验证)上提供一个证书,然后提供XMLHTTPRequest的另一个证书,我的扩展将会使。这将导致扩展修改网站内容基于不一致的信息。因此,我希望获得浏览器在验证网站时使用的SSL证书信息。
考虑到这一点,我将上述方法与概述的方法在中拦截所有HTTP响应,方法是添加一个http-on-examine-response事件的观察者。我认为用这种方法,我可以简单地获取从站点下载的证书信息。
这是我的代码的肉,其中大部分来自上面的链接(其余部分是Firefox扩展的样板):
$ b $ pre $函数dumpSecurityInfo(channel){
const Cc = Components.classes
const Ci = Components.interfaces;
//我们是否有有效的频道参数?
if(!channel instanceof Ci.nsIChannel){
dump(No channels available \\\
);
return;
}
var secInfo = channel.securityInfo;
$ b $ //打印一般连接安全状态
if(secInfo instanceof Ci.nsITransportSecurityInfo){
dump(name:+ channel.name +\\\
);
secInfo.QueryInterface(Ci.nsITransportSecurityInfo);
dump(\tSecurity state:);
$ b $ //检查安全状态标志
if((secInfo.securityState& Ci.nsIWebProgressListener.STATE_IS_SECURE)== Ci.nsIWebProgressListener.STATE_IS_SECURE)
dump(secure \ N); $(b)b
else if((secInfo.securityState& Ci.nsIWebProgressListener.STATE_IS_INSECURE)== Ci.nsIWebProgressListener.STATE_IS_INSECURE)
dump(insecure \\\
); ((secInfo.securityState& Ci.nsIWebProgressListener.STATE_IS_BROKEN)== Ci.nsIWebProgressListener.STATE_IS_BROKEN)
dump(unknown \\\
);
else if
dump(\ tSecurity description:+ secInfo.shortSecurityDescription +\\\
);
dump(\ tSecurity error message:+ secInfo.errorMessage +\\\
);
打印SSL证书详细信息
if(secInfo instanceof Ci.nsISSLStatusProvider){
var cert = secInfo.QueryInterface(Ci.nsISSLStatusProvider )。
SSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
dump(\\\
Certificate Status:\\\
);
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;
默认值:
dump(unexpected failure);
break;
}
dump(\\\
);
$ b dump(\ tCommon name(CN)=+ cert.commonName +\\\
);
dump(\tOrganisation =+ cert.organization +\\\
);
dump(\ tIssuer =+ cert.issuerOrganization +\\\
);
dump(\tSHA1 fingerprint =+ cert.sha1Fingerprint +\\\
);
var validity = cert.validity.QueryInterface(Ci.nsIX509CertValidity);
dump(\ tValid from+ validity.notBeforeGMT +\\\
);
dump(\ tValid until+ validity.notAfterGMT +\\\
);
函数TracingListener(){
}
TracingListener.prototype =
{
originalListener :null,
$ b onDataAvailable:function(request,context,inputStream,offset,count){
尝试
{
dumpSecurityInfo(请求)
this。 originalListener.onDataAvailable(request,context,inputStream,offset,count);
} catch(err){
dump(err);
if(err instanceof Ci.nsIException)
{
request.cancel(e.result);
$ b onStartRequest:function(request,context){
try
{
dumpSecurityInfo(request )
this.originalListener.onStartRequest(request,context);
} catch(err){
dump(err);
if(err instanceof Ci.nsIException)
{
request.cancel(e.result);
$ b onStopRequest:function(request,context,statusCode){
this.originalListener.onStopRequest(request,context,statusCode );
},
QueryInterface:function(aIID){
const Ci = Components.interfaces;
if(iid.equals(Ci.nsIObserver)||
iid.equals(Ci.nsISupportsWeakReference)||
iid.equals(Ci.nsISupports))
{
返回这个;
}
throw Components.results.NS_NOINTERFACE;
$ b var httpRequestObserver =
{
观察:函数(aSubject,aTopic,aData)
{
const Ci = Components.interfaces;
if(aTopic ==http-on-examine-response)
{
var newListener = new TracingListener();
Subject.QueryInterface(Ci.nsITraceableChannel);
newListener.originalListener = aSubject.setNewListener(newListener);
QueryInterface:function(aIID)
{
const Ci = Components.interfaces;
if(aIID.equals(Ci.nsIObserver)||
aIID.equals(Ci.nsISupports))
{
return this;
}
throw Components.results.NS_NOINTERFACE;
}
};
var test =
{
run:function(){
const Ci = Components.interfaces;
dump(run);
var observerService = Components.classes [@ mozilla.org/observer-service;1]
.getService(Ci.nsIObserverService);
observerService.addObserver(httpRequestObserver,
http-on-examine-response,false);
}
};
window.addEventListener(load,function(){test.run();},false);
我发现这个实现是不一致的。当我在Firefox中加载gmail.com时,我有时会得到证书信息,有时我不会。我怀疑这是一个缓存问题,因为刷新页面通常会导致证书信息被下载/打印。
对于我的预期应用程序,这种行为是不可接受的。这是一个研究项目,所以如果必须的话,我会愿意修改Firefox的源代码,但我的首选是使用扩展/附加API来做到这一点。
是否有更好,更一致的方法来获取SSL证书信息?
您查询频道以获取其安全信息的方式似乎很理智。我怀疑你的问题实际上是计时 - 你在错误的时间查询。跟踪所有请求实际上是错误的方法,如果安全信息是所有你感兴趣的。这是更有意义的注册进度监听器(有),并在调用 onSecurityChange
时查看频道。您可能只对 aState
包含。请注意, aRequest
参数通常是 nsIChannel
实例,但也可以是普通的 nsIRequest
-
instanceof
检查是必需的。
I'm trying to develop a Firefox extension/add-on that needs access to the SSL Certificate information of the page that is currently loaded. Once I have this information I plan on modifying the contents of the page based on the SSL information. Though, before I get there I first need to get the SSL info.
The approach outlined here makes a separate XMLHTTPRequest to get the security certificate. I would rather not do that if I could avoid it because it presents a security problem.
For example, a malicious site/man-in-the-middle could provide one certificate on the first request for the page (which the browser would verify) and then provide another certificate for the XMLHTTPRequest that my extension would make. This would result in the extension modifying site contents based on inconsistent information. Hence, I'd like to get the SSL Cert information that the browser itself used when verifying the site.
With that in mind I combined the above approach with the method outlined in Altering HTTP Responses in Firefox Extension to intercept all the HTTP responses by adding an observer of the "http-on-examine-response" event. I thought that with this method I could simply grab the cert info as it was being downloaded from the site.
Here is the meat of my code, much of it taken from the above links (the rest is Firefox extension boilerplate):
function dumpSecurityInfo(channel) {
const Cc = Components.classes
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
if (secInfo instanceof Ci.nsITransportSecurityInfo) {
dump("name: " + channel.name + "\n");
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");
}
// 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");
}
}
function TracingListener() {
}
TracingListener.prototype =
{
originalListener: null,
onDataAvailable: function(request, context, inputStream, offset, count) {
try
{
dumpSecurityInfo(request)
this.originalListener.onDataAvailable(request, context, inputStream, offset, count);
} catch (err) {
dump(err);
if (err instanceof Ci.nsIException)
{
request.cancel(e.result);
}
}
},
onStartRequest: function(request, context) {
try
{
dumpSecurityInfo(request)
this.originalListener.onStartRequest(request, context);
} catch (err) {
dump(err);
if (err instanceof Ci.nsIException)
{
request.cancel(e.result);
}
}
},
onStopRequest: function(request, context, statusCode) {
this.originalListener.onStopRequest(request, context, statusCode);
},
QueryInterface: function (aIID) {
const Ci = Components.interfaces;
if ( iid.equals(Ci.nsIObserver) ||
iid.equals(Ci.nsISupportsWeakReference) ||
iid.equals(Ci.nsISupports))
{
return this;
}
throw Components.results.NS_NOINTERFACE;
}
}
var httpRequestObserver =
{
observe: function(aSubject, aTopic, aData)
{
const Ci = Components.interfaces;
if (aTopic == "http-on-examine-response")
{
var newListener = new TracingListener();
aSubject.QueryInterface(Ci.nsITraceableChannel);
newListener.originalListener = aSubject.setNewListener(newListener);
}
},
QueryInterface : function (aIID)
{
const Ci = Components.interfaces;
if (aIID.equals(Ci.nsIObserver) ||
aIID.equals(Ci.nsISupports))
{
return this;
}
throw Components.results.NS_NOINTERFACE;
}
};
var test =
{
run: function() {
const Ci = Components.interfaces;
dump("run");
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
observerService.addObserver(httpRequestObserver,
"http-on-examine-response", false);
}
};
window.addEventListener("load", function () { test.run(); }, false);
What I found is that this implementation is inconsistent. When I load gmail.com in Firefox I'll sometimes get the certificate information and sometimes I won't. I suspect this is a caching issue as refreshing the page will usually result in the certificate information being downloaded/printed.
For my intended application this behavior is not acceptable. This is for a research project so, if I have to, I would be willing to modify the Firefox source code, but my preference would be to do this using the extension/add-on API.
Is there a better, more consistent way to get the SSL Certificate information?
The way you query the channel to get its security info seems sane. I suspect that your problem is actually timing - you query it at the wrong time. Tracing all requests is really the wrong approach if security info is all you are interested in. It makes far more sense to register a progress listener (there are examples) and to look at the channel whenever onSecurityChange
is being called. You are likely to be only interested in requests where aState
contains STATE_IS_SECURE
flag. Note that aRequest
parameter is usually an nsIChannel
instance but could also be a plain nsIRequest
- instanceof
check is required.
这篇关于如何获得Firefox扩展中* current *页的SSL证书信息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!