2011-08-08 24 views
5

Tôi đang cố gắng phát triển tiện ích mở rộng/tiện ích bổ sung của Firefox cần truy cập vào thông tin Chứng chỉ SSL của trang hiện được tải . Khi tôi có thông tin này, tôi dự định sửa đổi nội dung của trang dựa trên thông tin SSL. Mặc dù, trước khi tôi đến đó, trước tiên tôi cần có thông tin SSL.Làm cách nào để lấy thông tin Chứng chỉ SSL cho trang * hiện tại * trong Firefox Thêm Vào

Cách tiếp cận được nêu here tạo XMLHTTPRequest riêng biệt để nhận chứng chỉ bảo mật. Tôi không muốn làm điều đó nếu tôi có thể tránh nó vì nó trình bày một vấn đề an ninh.

Ví dụ: trang web độc hại/người ở giữa có thể cung cấp một chứng chỉ về yêu cầu đầu tiên cho trang (trình duyệt sẽ xác minh) và sau đó cung cấp chứng chỉ khác cho XMLHTTPRequest mà tiện ích của tôi sẽ thực hiện. Điều này sẽ dẫn đến việc mở rộng sửa đổi nội dung trang web dựa trên thông tin không phù hợp. Do đó, tôi muốn nhận thông tin SSL Cert mà trình duyệt tự sử dụng khi xác minh trang web.

Với ý nghĩ đó, tôi kết hợp cách tiếp cận trên với phương pháp được nêu trong Altering HTTP Responses in Firefox Extension để chặn tất cả các phản hồi HTTP bằng cách thêm người quan sát sự kiện "http-on-exam-response". Tôi nghĩ rằng với phương pháp này tôi có thể chỉ cần lấy các thông tin cert như nó đã được tải xuống từ trang web.

Đây là thịt của mã của tôi, phần lớn là lấy từ các liên kết trên (phần còn lại là mở rộng của Firefox soạn sẵn):

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

Những gì tôi thấy là thực hiện này là không phù hợp. Khi tôi tải gmail.com trong Firefox, đôi khi tôi sẽ nhận được thông tin chứng chỉ và đôi khi tôi sẽ không. Tôi nghi ngờ đây là một vấn đề bộ nhớ đệm như làm mới trang thường sẽ dẫn đến thông tin chứng chỉ được tải xuống/in.

Đối với ứng dụng dự định, hành vi này không được chấp nhận. Đây là một dự án nghiên cứu vì vậy, nếu tôi phải, tôi sẽ sẵn sàng sửa đổi mã nguồn Firefox, nhưng ưu tiên của tôi sẽ là làm điều này bằng cách sử dụng API tiện ích mở rộng/tiện ích bổ sung.

Có cách nào tốt hơn, nhất quán hơn để nhận thông tin Chứng chỉ SSL không?

+1

Bạn không nên nuốt lỗi trong 'bạn TracingListener'. Tôi đã từng làm điều này và nhận thấy rằng nó gây ra sự cố do trạng thái không nhất quán. Nếu người nghe ban đầu ném lỗi và bạn không muốn giữ nó (do lỗi trong Bảng điều khiển Lỗi) thì yêu cầu đó sẽ bị hủy. Như thế này: 'catch (e if e instanceof Ci.nsIException) {request.cancel (e.result);}' –

+0

Tôi đã thực hiện một số chỉnh sửa cho câu hỏi theo đề xuất của bạn. Điều đó có xử lý trường hợp bạn mô tả không? –

+0

Có, cách này sẽ hoạt động chính xác. –

Trả lời

3

Cách bạn truy vấn kênh để nhận thông tin bảo mật có vẻ lành mạnh. Tôi nghi ngờ rằng vấn đề của bạn thực sự là thời gian - bạn truy vấn nó vào thời điểm sai. Truy tìm tất cả các yêu cầu thực sự là cách tiếp cận sai nếu thông tin bảo mật là tất cả những gì bạn quan tâm. Nó có ý nghĩa hơn khi đăng ký một người nghe tiến độ (có examples) và xem kênh bất cứ khi nào onSecurityChange đang được gọi. Bạn có thể chỉ quan tâm đến các yêu cầu trong đó aState chứa STATE_IS_SECURE flag. Lưu ý rằng thông số aRequest thường là trường hợp nsIChannel nhưng cũng có thể là yêu cầu kiểm tra đơn giản là nsIRequest - instanceof.

+0

Lưu ý 'onSecurityChange' hiện là một API cũ và không còn hoạt động trong Firefox hiện tại: https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Code_snippets/Progress_Listeners – mikemaccana

+0

@mikemaccana: Khá nhiều thứ giờ đây là một API cũ và không còn hoạt động nữa. Mọi thông tin trước năm 2017 về các tiện ích bổ sung của Firefox đều lỗi thời. –

3

xây dựng trên this câu trả lời:

Bí quyết là để đăng ký một progress listener và kiểm tra aState khi hàm onSecurityChange được gọi. Nếu cờ Ci.nsIWebProgressListener.STATE_IS_SECURE được đặt thì trang đang sử dụng kết nối SSL. Tuy nhiên, điều đó không đủ, thông số aRequest có thể không phải là phiên bản Ci.nsIChannel, thông tin đó phải được xác minh trước tiên với if (aRequest instanceof Ci.nsIChannel).

Đây là mã làm việc:

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); 
Các vấn đề liên quan