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

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

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

笔记:

  1. 没有可能用Apache或其他东西来处理它。 它必须在节点中完成。

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

您将如何解决?

Elias asked 2019-11-07T12:39:22Z
8个解决方案
51 votes

万一您托管在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);
Lavamantis answered 2019-11-07T12:39:48Z
26 votes

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

options

options..一堆提示

options

将证书和密钥文件存储在包含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);

完成!

MeetM answered 2019-11-07T12:40:45Z
21 votes

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

在这种限制下,由于未提供原因,您的问题似乎是客户端以某种方式连接到错误的端口。 这是一种奇怪的情况,因为默认情况下,客户端将向端口80发出HTTP请求,向端口443发出HTTPS。当我说“默认”时,是指URL中是否不包含特定端口。 因此,除非您明确使用[http://example.com:443]和[https://example.com:80]之类的纵横交错的URL,否则您的网站确实不应有纵横交错的流量。 但是,既然您问了这个问题,尽管您打赌您使用的是非标准端口而不是默认的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,这对于 通过路由中间件链发送。 我认为Express堆栈甚至无法获得它们,因为它们不是有效的请求,因此它们在节点TCP堆栈中的某个位置被忽略。 可能可以编写服务器来执行此操作,并且可能已经有一个模块了,但是您必须直接在TCP层上编写它。 而且,您必须在命中TCP端口的第一批客户端数据中检测到常规HTTP请求,并将该连接连接到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
Peter Lyons answered 2019-11-07T12:41:55Z
10 votes

基于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);
  }
});
Jonathan Tran answered 2019-11-07T12:42:21Z
3 votes

http.createServer(app.handle).listen(80)

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

快递2倍

TJ Holowaychuk answered 2019-11-07T12:43:00Z
2 votes

该代码看起来像它所需要的:[https://gist.github.com/903596]

robocat answered 2019-11-07T12:43:25Z
2 votes

试试这个例子:

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);
        });
Ashok Kumawat answered 2019-11-07T12:43:53Z
-1 votes

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

编辑:更新了网址

Elias answered 2019-11-07T12:44:27Z
translate from https://stackoverflow.com:/questions/8605720/how-to-force-ssl-https-in-express-js