40

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

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

谢谢,杰夫

4

3 回答 3

60

是的,可以使用一台服务器来完成。但需要注意的是,它适用于支持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>);

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

于 2013-11-29T12:33:13.877 回答
27

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

tls.createServer()必须在选项中有keyand cert,因为它们在手册中是必需的。如果不支持,也许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 请求路由到您的各种应用程序。

于 2016-08-10T00:58:56.023 回答
3

有人在greenlock-express.js中提出了一个问题并引用了这篇文章,所以我将在此处包含使用Greenlock 进行此操作的方法,以便让我们加密

将 Greenlock.js 用于动态 SSL 证书

Greenlock 完全满足您的需求,但同时兼顾安全性和便利性。

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

安装

npm install --save greenlock-express

通过 Greenlock 使用 Let's Encrypt

require("greenlock-express")
    .init(function getConfig() {
        return { package: require("./package.json") };
    })
    .serve(httpsWorker);

function httpsWorker(server) {
    // Works with any Node app (Express, etc)
    var app = require("./my-express-app.js");

    // See, all normal stuff here
    app.get("/hello", function(req, res) {
        res.end("Hello, Encrypted World!");
    });

    // Serves on 80 and 443
    // Get's SSL certificates magically!
    server.serveApp(app);
}

文档

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

重要说明:安全注意事项

Greenlock 已经缓解了这些安全问题,但如果您是手动实施,您应该知道一些事情来保证安全:

特别是,当您使用您自己编写的代码动态加载 ssl 证书时,您可能会使自己容易受到SQL 注入和/或定时攻击,这一点非常重要。

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

如果您有兴趣了解该漏洞利用如何发挥作用,我已在Greenlock for node.js 第 3 部分:安全问题https://github.com/nodejs/node/issues/22389中记录了它

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

于 2018-08-23T05:19:38.540 回答