2012-12-30 87 views
17

我試圖創建一個使用Node.js的0.8.8使用自簽名證書的TLS服務器/客戶端安裝。主機名/ IP不匹配證書的備用名

基本服務器代碼看起來像

var tlsServer = tls.createServer({ 
    key: fs.readFileSync('server-key.pem'), 
    cert: fs.readFileSync('server-cert.pem') 
}, function (connection) { 
    // [...] 
}); 
tlsServer.listen(3000); 

現在,當我嘗試連接到這臺服務器我使用下面的代碼:

var connection = tls.connect({ 
    host: '192.168.178.31', 
    port: 3000, 

    rejectUnauthorized: true, 
    ca: [ fs.readFileSync('server-cert.pem') ] 
}, function() { 
    console.log(connection.authorized); 
    console.log(connection.authorizationError); 
    console.log(connection.getPeerCertificate()); 
}); 

如果我刪除線

ca: [ fs.readFileSync('server-cert.pem') ] 

從客戶端代碼,Node.js的拋出一個錯誤告訴我DEPTH_ZERO_SELF_SIGNED_CERT。據我瞭解,這是由於這是一個自簽名證書,並且沒有其他方信任此證書。

如果我刪除

rejectUnauthorized: true, 

爲好,錯誤消失 - 但connection.authorized等於false這實際上意味着我的連接未加密。無論如何,使用getPeerCertificate()我可以訪問服務器發送的證書。由於我想執行加密連接,因此我明白我可能不會刪除此行。

現在我讀,我可以使用ca屬性來指定,我想Node.js的信任任何CA。該documentation of the TLS module意味着它足以服務器證書添加到ca數組,然後一切都應該罰款。

如果我這樣做,這個錯誤是走了,但我得到一個新問題:

Hostname/IP doesn't match certificate's altnames 

對我來說,這意味着CA現在基本可信的,因此,現在沒事,但證書被做對於另一臺主機而不是我使用的主機。

我創建使用

​​

的文檔暗示的證書。在創建企業社會責任時,我會問到一些常見的問題,例如國家,州,......和通用名稱(CN)。正如您在「網絡上」所告訴您的SSL證書所做的那樣,而不是會提供您的名稱作爲CN,但是您希望使用的主機名。

而這可能是我失敗的地方。

我試圖

  • localhost
  • 192.168.178.31
  • eisbaer
  • eisbaer.fritz.box

其中最後兩個是當地的名字和我的機器的完全合格的本地名稱。

任何想法我在做什麼錯在這裏?

+0

重要提示:這是'Node v0.8.8'。 'rejectUnauthorized'現在默認爲'true'。 – Breedly

+0

如果您使用'rejectUnauthorized:false',則默認情況下您的連接[仍然是加密的](https://stackoverflow.com/a/16311147/4068278)。它根本不驗證您連接的主機的身份,這可能會讓您容易受到SSL上的MITM攻擊。 –

回答

13

最近有一個addition to node.js它允許覆蓋與自定義函數的主機名檢查。它已添加到v0.11.14,並將在下一個穩定版本(0.12)中提供。現在您可以執行如下操作:

var options = { 
    host: '192.168.178.31', 
    port: 3000, 
    ca: [ fs.readFileSync('server-cert.pem') ], 
    checkServerIdentity: function (host, cert) { 
    return undefined; 
    } 
}; 
options.agent = new https.Agent(options); 
var req = https.request(options, function (res) { 
    //... 
}); 

這將現在接受任何服務器身份,但仍然會加密連接並驗證密鑰。

注意,在以前的版本(例如v0.11.14),該checkServerIdentity是返回一個boolean表示服務器的有效性。如果存在問題,則已更改(在v4.3.1之前)功能return(不是throw),如果有問題則爲undefined

+0

被困在v0.10中,我[猴子補丁](https://gist.github.com/ntrrgc/c5cbff08d0033e00f503)'tls'模塊跳過這個檢查。這是一個非常醜陋的解決方案,因爲它是全球性的,但它總比沒有好。 – Alicia

+6

set NODE_TLS_REJECT_UNAUTHORIZED = 0 env var –

+6

只是爲了警告人們:'NODE_TLS_REJECT_UNAUTHORIZED'允許你連接到任何人,不管它是否是你想要的服務器。連接是加密的,但它不是*授權*,這通常是非常關鍵的事情。而且,將它設置爲全局變量是一個非常笨手段的方法。只需在'tls'連接選項中將'rejectUnauthorized'設置爲false即可。 – Breedly

8

tls.js, lines 112-141中,您可以看到,如果調用connect時使用的主機名是IP地址,則證書的CN將被忽略,只有SAN正在使用。

由於我的證書不使用SAN,驗證失敗。

+0

我有這個相同的問題,但我有本地主機和127.0的SAN。0.1。無論我與主機名('localhost')還是IP地址('127.0.0.1')連接,都無關緊要。對於我來說,openssl s_client連接給定所有相同的參數,而tls.connect()失敗。 – Bryan

0

你做錯了什麼是使用IP地址而不是域名。創建一個域名,並將其粘貼到DNS服務器(或僅存在於主機文件中),創建一個域名爲公用名的自簽名證書,並連接到域名而不是IP地址。

+4

但它應該(必須)與IP地址一起工作,不是嗎?出於各種原因,我不想設置域名。 –

7

如果您使用的是主機名進行連接,則會根據DNS類型的主題備用名稱(如果有)來檢查主機名,否則將返回到主題專有名稱中的CN上。

如果您使用IP地址進行連接,IP地址將與IP地址類型的SAN進行檢查,而不會退回到CN上。

這至少符合HTTP over TLS規範(即HTTPS)的實現。有些瀏覽器有點寬容。

這與this answer in Java中的問題完全相同,該問題也提供了通過OpenSSL放置定製SAN的方法(請參閱this document too)。

一般來說,除非是用於測試CA,否則很難管理依賴IP地址的證書。使用主機名連接更好。

4

Mitar有一個錯誤的假設,即checkServerIdentity應該在成功時返回'true',但實際上它應該在成功時返回'undefined'。任何其他值都被視爲錯誤描述。

所以這樣的代碼是正確的:

var options = { 
    host: '192.168.178.31', 
    port: 3000, 
    ca: [ fs.readFileSync('server-cert.pem') ], 
    checkServerIdentity: function (host, cert) { 
    // It can be useful to resolve both parts to IP or to Hostname (with some synchronous resolver (I wander why they did not add done() callback as the third parameter)). 
    // Be carefull with SNI (when many names are bound to the same IP). 
    if (host != cert.subject.CN) 
     return 'Incorrect server identity';// Return error in case of failed checking. 
     // Return undefined value in case of successful checking. 
     // I.e. you could use empty function body to accept all CN's. 
    } 
}; 
options.agent = new https.Agent(options); 
var req = https.request(options, function (res) { 
    //... 
}); 

我想只是爲了在米塔爾的答案編輯,但編輯被拒絕,所以我創建了一個分開的答案。

相關問題