2011-08-08 4 views
5

現在ロードされているページのSSL証明書情報へのアクセスが必要なFirefox拡張機能/アドオンを開発しようとしています。この情報を取得したら、SSL情報に基づいてページの内容を変更する予定です。しかし、私がそこに着く前にまずSSL情報を入手する必要があります。Firefoxの*現在のページのSSL証明書情報を入手するにはどうすればいいですか?アドオン

アプローチは、セキュリティ証明書を取得するために別のXMLHTTPRequestをしますhereを概説しました。私はそれがセキュリティの問題を提示するので、私はそれを避けることができたら、私はむしろそれをしないだろう。

例えば、悪意のあるサイト/中間者は、ブラウザが確認するページの最初のリクエストで1つの証明書を提供し、次に自分の拡張が行うXMLHTTPRequestの別の証明書を提供する可能性があります。これにより、矛盾した情報に基づいてサイトの内容を変更する拡張が行われます。したがって、サイトを確認するときにブラウザ自体が使用したSSL証明書情報を取得したいと考えています。

これを念頭に置いて、私はAltering HTTP Responses in Firefox Extensionで概説された方法と上記の方法を組み合わせて、「http-on-examine-response」イベントのオブザーバーを追加することによってすべてのHTTP応答をインターセプトします。私は、この方法では、サイトからダウンロードされたときに証明書情報を取得することができたと考えました。どのような私が見つけたことは、この実装に一貫性がないということである

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); 

:ここ

は、その多くが、上記のリンク(残りはFirefoxの拡張機能の定型である)から取られた、私のコードの肉です。 Firefoxでgmail.comを読み込むと、時には証明書の情報が得られることもあります。ページを更新すると、通常、証明書情報がダウンロード/印刷されるため、これはキャッシングの問題と思われます。

私の意図するアプリケーションでは、この動作は受け入れられません。これは研究プロジェクトのためのものです。もし必要ならば、私はFirefoxのソースコードを変更したいと思っていますが、私の好みは拡張機能/アドオンAPIを使ってこれを行うことです。

SSL証明書情報を取得するためのより良い、より一貫した方法がありますか?

+1

あなたの 'TracingListener'のエラーを飲み込むべきではありません。私はこれをして、それが矛盾した状態のためにクラッシュを引き起こすことに気づいた。元のリスナーがエラーをスローし、それを保持したくない場合(エラーコンソールのスパムのため)、リクエストをキャンセルする必要があります。このように: 'catch(e if instance if Ci.nsIException){request.cancel(e.result);}' –

+0

あなたの提案に応じて質問を編集しました。それは、あなたが描いた事件を処理しますか? –

+0

はい、このように正しく動作するはずです。 –

答えて

3

セキュリティ情報を取得するためにチャネルを照会する方法はまったく見えません。あなたの問題は実際にはタイミングが間違っていると思われます。すべてのリクエストを追跡することは、セキュリティ情報が関心のあるすべてのものであれば、間違ったアプローチです.が呼び出されているときは、進捗リスナー(examples)を登録し、チャネルを確認する方がはるかに意味があります。 aStateSTATE_IS_SECURE flagが含まれているリクエストのみに興味がある可能性があります。 aRequestパラメータは通常nsIChannelインスタンスですが、平文であることもあります。nsIRequest - instanceofチェックが必要です。

+0

メモ 'onSecurityChange'はレガシーAPIになっており、現在のFirefoxでは動作しません:https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Code_snippets/Progress_Listeners – mikemaccana

+0

@mikemaccana:ほとんどすべてが従来のAPIになっていて、もはや動作しません。 Firefoxアドオンに関する2017年以前の情報は廃止されました。 –

3

this答えのビル:

トリックがprogress listenerを登録し、onSecurityChange関数が呼び出されたときにaStateをチェックすることです。 Ci.nsIWebProgressListener.STATE_IS_SECUREフラグが設定されている場合、ページはSSL接続を使用しています。しかしそれだけでは不十分ですが、aRequestパラメータはCi.nsIChannelのインスタンスではない可能性があります。最初にif (aRequest instanceof Ci.nsIChannel)で検証する必要があります。ここで

は、作業コードです:

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"); 
    } 
    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"); 
    } 
} 

var myListener = 
{ 
    QueryInterface: function(aIID) 
    { 
     if (aIID.equals(Components.interfaces.nsIWebProgressListener) || 
      aIID.equals(Components.interfaces.nsISupportsWeakReference) || 
      aIID.equals(Components.interfaces.nsISupports)) 
      return this; 
     throw Components.results.NS_NOINTERFACE; 
    }, 

    onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) { }, 

    onLocationChange: function(aProgress, aRequest, aURI) { }, 

    onProgressChange: function(aWebProgress, aRequest, curSelf, maxSelf, curTot, maxTot) { }, 
    onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) { }, 
    onSecurityChange: function(aWebProgress, aRequest, aState) 
    { 
     // check if the state is secure or not 
     if(aState & Ci.nsIWebProgressListener.STATE_IS_SECURE) 
     { 
      // this is a secure page, check if aRequest is a channel, 
      // since only channels have security information 
      if (aRequest instanceof Ci.nsIChannel) 
      { 
       dumpSecurityInfo(aRequest); 
      } 
     }  
    } 
} 

var test = 
{ 
    run: function() { 
     dump("run\n"); 
     gBrowser.addProgressListener(myListener); 
    } 
}; 

window.addEventListener("load", function() { test.run(); }, false); 
関連する問題