是否可以在NodeJS中动态返回SSL证书?

时间:2012-08-31 17:00:30

标签: node.js ssl-certificate

我想在我的NodeJS应用程序中动态返回ssl证书信息。我有两个链接到同一节点应用程序的域名。我只看到在创建服务器时可以指定ssl设置。是否可以根据请求的URL动态返回ssl证书?

否则,如果我必须在另一个端口上创建第二个服务器实例,我是否能够将每个请求透明地传送到原始端口?我可以看起来它没有在第二个端口上运行吗?

谢谢, 杰夫

3 个答案:

答案 0 :(得分:47)

是的,可以使用一台服务器来完成。但需要注意的是,它适用于支持SNI的客户端 - 这是最现代的浏览器。

您就是这样做的:

//function to pick out the key + certs dynamically based on the domain name
function getSecureContext (domain) {
    return crypto.createCredentials({
        key:  fs.readFileSync('/path/to/domain.key'),
        cert: fs.readFileSync('/path/to/domain.crt'),
        ca: [fs.readFileSync('/path/to/CA_cert_1.crt'), fs.readFileSync('/path/to/CA_cert_2.crt'), <include all CA certs that you have to> ... ]
      }).context;
}

//read them into memory
var secureContext = {
    'domain1': getSecureContext('domain1'),
    'domain2': getSecureContext('domain2'),
    .
    .
}

//provide a SNICallback when you create the options for the https server
var options = {
    SNICallback: function (domain) {
        return secureContext[domain];
    }, //SNICallback is passed the domain name, see NodeJS docs on TLS
    cert: fs.readFileSync('/path/to/server.crt'),
    key: fs.readFileSync('/path/to/server.key'),                
    }
}

//create your https server
var server = require('https').createServer(options, [requestListener]);
//using Express
var server = require('https').createServer(options, require('express')());
server.listen(<someport>);

这是有效的,因为the options for https类似于tls.createServer()。确保在crypto.createCredentials调用中包含所有必需的CA中间证书和根证书。此外,如果您有CA捆绑包,请在使用它们之前将它们拆分为多个单个crt文件,因为'ca'接受一组证书。

答案 1 :(得分:18)

不推荐使用

crypto.createCredentials(),因此请改用tls.createSecureContext()

tls.createServer()必须在选项中包含keycert,因为手册中需要它们。如果不支持tls.createServer()SNICallback可能会将这些参数用作默认值。

var secureContext = {
    'mydomain.com': tls.createSecureContext({
        key: fs.readFileSync('../path_to_key1.pem', 'utf8'),
        cert: fs.readFileSync('../path_to_cert1.crt', 'utf8'),
        ca: fs.readFileSync('../path_to_certificate_authority_bundle.ca-bundle1', 'utf8'), // this ca property is optional
    }),
    'myotherdomain.com': tls.createSecureContext({
        key: fs.readFileSync('../path_to_key2.pem', 'utf8'),
        cert: fs.readFileSync('../path_to_cert2.crt', 'utf8'),
        ca: fs.readFileSync('../path_to_certificate_authority_bundle.ca-bundle2', 'utf8'), // this ca property is optional
    }),
}
try {
    var options = {
        SNICallback: function (domain, cb) {
            if (secureContext[domain]) {
                if (cb) {
                    cb(null, secureContext[domain]);
                } else {
                    // compatibility for older versions of node
                    return secureContext[domain]; 
                }
            } else {
                throw new Error('No keys/certificates for domain requested');
            }
        },
       // must list a default key and cert because required by tls.createServer()
        key: fs.readFileSync('../path_to_key.pem'), 
        cert: fs.readFileSync('../path_to_cert.crt'), 
    }
    https.createServer(options, function (req, res) {
        res.end('Your dynamic SSL server worked!')
        // Here you can put proxy server routing here to send the request 
        // to the application of your choosing, running on another port.
        // node-http-proxy is a great npm package for this
    }).listen(443);
} catch (err){
    console.error(err.message);
    console.error(err.stack);
}

在服务器内部,您可以使用nodejs包http-proxy将您的https请求路由到各种应用程序。

答案 2 :(得分:1)

有人在greenlock-express.js中提出了一个问题,并引用了此帖子,因此,我还将在此处包括 Greenlock for Let's Encrypt 的实现方法:

将Greenlock.js用于动态SSL证书

Greenlock完全可以满足您的需求,但是在安全性和便利性上却是如此。

  • 使用结构化目录路径动态加载tls证书
  • 通过Let's Encrypt v2自动颁发和更新SSL证书
  • 防御SNI和主机攻击以及域前沿。

安装

npm install --save greenlock-express

通过Greenlock使用“让我们加密”

'use strict';

require('greenlock-express').create({
  version: 'draft-11'
, server: 'https://acme-v02.api.letsencrypt.org/directory'
, configDir: '~/.config/acme/'


  // You MUST change these to valid domains
  // NOTE: all domains will be validated and listed on the certificate
, approveDomains: [ 'example.com', 'www.example.com' ]


, email: 'CHANGE_ME@example.com' // For Let's Encrypt emails and Greenlock security updates
, agreeTos: true                 // For Let's Encrypt ToS
, communityMember: true          // for relevant, but non-critical greenlock updates
, telemetry: true                // contribute to project telemetry data

, app: require('./my-express-app')
//, debug: true
});

var server = greenlock.listen(80, 443);

文档

视频部分专门与动态域加载的配置有关:2:26 Greenlock for node.js Part 2: Configuration

重要注意事项:安全注意事项

Greenlock已经缓解了这些安全问题,但是如果您要手动实施,请注意一些事项以保持安全:

特别重要的是,要知道当您使用自己编写的代码动态加载ssl证书时,可以make yourself vulnerable进行SQL注入和/或定时攻击。 >

尽管您希望像example.com这样的有效字节会通过节点的tls.SNICallback(sni, cb)req.socket.servername,但实际上您可以从Robert'); DROP TABLE Students;(或很少的Bobby Tables)访问我们喜欢称呼他)。

如果您有兴趣了解该漏洞利用的工作方式,请在Greenlock for node.js Part 3: Security Concernshttps://github.com/nodejs/node/issues/22389

中进行记录。

您还可能容易受到Domain Fronting的攻击,这是一种低风险的攻击/旁道,但了解和理解很重要。