如何在Express.js中强制使用SSL / https

时间:2011-12-22 15:03:39

标签: node.js redirect ssl https express

我正在尝试为Express.js创建一个中间件,将所有非安全(端口80)流量重定向到安全的SSL端口(443)。 不幸的是,Express.js请求中没有任何信息可以让您确定请求是通过http还是https。

一种解决方案是重定向每个请求,但这不是我的选择。

注意:

  1. 使用Apache或其他方法无法处理它。 要在节点中完成。

  2. 只能在应用程序中启动一个服务器。

  3. 你会如何解决这个问题?

8 个答案:

答案 0 :(得分:40)

万一你在Heroku上托管并且只想重定向到HTTPS而不管端口,这里是我们正在使用的中间件解决方案。

如果您在本地开发,则无需重定向。

function requireHTTPS(req, res, next) {
  // The 'x-forwarded-proto' check is for Heroku
  if (!req.secure && req.get('x-forwarded-proto') !== 'https' && process.env.NODE_ENV !== "development") {
    return res.redirect('https://' + req.get('host') + req.url);
  }
  next();
}

您可以像Express(2.x和4.x)一样使用它:

app.use(requireHTTPS);

答案 1 :(得分:25)

虽然问题看起来已经有一年了,但我想回答一下,因为它可能对其他人有所帮助。使用最新版本的expressjs(2.x)实际上非常简单。首先使用此代码创建密钥和证书

openssl genrsa -out ssl-key.pem 1024

$ openssl req -new -key ssl-key.pem -out certrequest.csr ..一堆提示

$ openssl x509 -req -in certrequest.csr -signkey ssl-key.pem -out ssl-cert.pem

将证书和密钥文件存储在包含app.js的文件夹中。然后编辑app.js文件并在express.createServer()

之前编写以下代码
var https = require('https');
var fs = require('fs');

var sslkey = fs.readFileSync('ssl-key.pem');
var sslcert = fs.readFileSync('ssl-cert.pem')

var options = {
    key: sslkey,
    cert: sslcert
};

现在传递createServer()函数中的options对象

express.createServer(options);

完成!

答案 2 :(得分:21)

首先,让我看看能否澄清问题。您只能使用一(1)个node.js进程,但该进程可以侦听两(2)个网络端口,包括80和443,对吗? (当您说一个服务器时,不清楚您是指一个进程还是仅一个网络端口。)

鉴于这种约束,你问题似乎是,由于你没有提供的原因,你的客户端以某种方式连接到错误的端口。这是一个奇怪的边缘情况,因为默认情况下,客户端将向端口80和HTTPS发出HTTP请求到端口443.当我说“默认情况下”时,我的意思是如果URL中没有包含特定端口。因此,除非您明确使用像http://example.com:443https://example.com:80这样的纵横交错的网址,否则您的网站不应该有任何交叉的流量。但是既然你问了这个问题,我猜你必须拥有它,虽然我打赌你使用的是非标准端口而不是80/443默认端口。

因此,对于后台:是的,一些Web服务器可以很好地处理这个问题。例如,如果您对nginx执行http://example.com:443,它将响应HTTP 400“错误请求”响应,指示“正常HTTP请求已发送到HTTPS端口”。是的,您可以从同一个node.js进程中侦听80和443。您只需要创建2个express.createServer()的单独实例,这样就没问题了。这是一个简单的程序来演示处理这两种协议。

var fs = require("fs");
var express = require("express");

var http = express.createServer();

var httpsOptions = {
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem')
};

var https = express.createServer(httpsOptions);

http.all('*', function(req, res) {
  console.log("HTTP: " + req.url);
  return res.redirect("https://" + req.headers["host"] + req.url);
});

http.error(function(error, req, res, next) {
  return console.log("HTTP error " + error + ", " + req.url);
});

https.error(function(error, req, res, next) {
  return console.log("HTTPS error " + error + ", " + req.url);
});

https.all('*', function(req, res) {
  console.log("HTTPS: " + req.url);
  return res.send("Hello, World!");
});

http.listen(80);

我可以通过cURL测试这个:

$ curl --include --silent http://localhost/foo
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Content-Type: text/html
Location: https://localhost/foo
Connection: keep-alive
Transfer-Encoding: chunked

<p>Moved Temporarily. Redirecting to <a href="https://localhost/foo">https://localhost/foo</a></p>

$ curl --include --silent --insecure https://localhost:443/foo
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 13
Connection: keep-alive

Hello, World!%

显示从HTTP到HTTPS的重定向......

curl --include --silent --location --insecure 'http://localhost/foo?bar=bux'
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Content-Type: text/html
Location: https://localhost/foo?bar=bux
Connection: keep-alive
Transfer-Encoding: chunked

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 13
Connection: keep-alive

Hello, World!%

这样就可以为常规案例提供两种协议并正确地重定向。但是,十字架根本不起作用。我相信一个快速交叉的请求命中快速服务器不会通过中间件堆栈路由,因为它从一开始就被视为错误,甚至不会正确解析请求URI,这是必要的通过路由中间件链发送它。快速堆栈甚至没有得到它们我认为因为它们不是有效请求,所以它们在节点TCP堆栈中的某处被忽略。可能编写一个服务器来执行此操作,并且可能已经有一个模块,但您必须直接在TCP层编写它。并且您必须在第一块客户端数据中检测到常规HTTP请求,该数据块访问TCP端口并将该连接连接到HTTP服务器,而不是正常的TLS握手。

当我执行其中任何一项操作时,我的快速错误处理程序不会被调用。

curl  --insecure https://localhost:80/foo
curl: (35) Unknown SSL protocol error in connection to localhost:80

curl http://localhost:443/foo
curl: (52) Empty reply from server

答案 3 :(得分:10)

基于Elias的答案,但内联代码。如果nginx或负载均衡器后面有节点,则此方法有效。 Nginx或负载均衡器将始终使用普通的旧http命中节点,但它会设置一个标题,以便您区分。

app.use(function(req, res, next) {
  var schema = req.headers['x-forwarded-proto'];

  if (schema === 'https') {
    // Already https; don't do anything special.
    next();
  }
  else {
    // Redirect to https.
    res.redirect('https://' + req.headers.host + req.url);
  }
});

答案 4 :(得分:3)

http.createServer(app.handle)。听(80)

https.createServer(options,app.handle).listen(443)

表示快递2x

答案 5 :(得分:2)

此代码看起来像您需要的那样:https://gist.github.com/903596

答案 6 :(得分:2)

试试这个例子:

var express = require('express');
        var app = express();
        // set up a route to redirect http to https
        app.use(function (req, res, next) {
        if (!/https/.test(req.protocol)) {
            res.redirect("https://" + req.headers.host + req.url);
        } else {
            return next();
        }
        });
        var webServer = app.listen(port, function () {
            console.log('Listening on port %d', webServer.address().port);
        });

答案 7 :(得分:-1)

由于我正在使用nginx,因此我可以访问标头的x-forwarded-proto属性,因此我可以编写一个微型中间件来重定向所有流量,如下所述:http://elias.kg/post/14971446990/force-ssl-with-express-js-on-heroku-nginx

编辑:更新了网址