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

Eli*_*ias 57 ssl https redirect node.js express

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

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

笔记:

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

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

你会如何解决这个问题?

Lav*_*tis 51

万一你在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();
}
Run Code Online (Sandbox Code Playgroud)

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

app.use(requireHTTPS);
Run Code Online (Sandbox Code Playgroud)

  • 对于我们当中的初学者:请记住将 `app.use(requireHTTPS)` 调用放在任何 `app.get` 调用之前(在任何应该由 SSL 保护的内容之前)。 (9认同)

Mee*_*etM 26

虽然问题看起来已经有一年了,但我想回答一下,因为它可能对其他人有所帮助.使用最新版本的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
};
Run Code Online (Sandbox Code Playgroud)

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

express.createServer(options);
Run Code Online (Sandbox Code Playgroud)

完成!


Pet*_*ons 21

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

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

因此,对于背景:是的,一些Web服务器处理得相当好.例如,如果您对http://ginample.com:443执行nginx,它将响应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);
Run Code Online (Sandbox Code Playgroud)

我可以通过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!%
Run Code Online (Sandbox Code Playgroud)

并显示从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!%
Run Code Online (Sandbox Code Playgroud)

因此,这将适用于常规案例的两个协议并正确地重定向.但是,十字架根本不起作用.我相信一个快速交叉的请求命中快速服务器不会通过中间件堆栈路由,因为它从一开始就被视为错误,甚至不会正确解析请求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
Run Code Online (Sandbox Code Playgroud)

  • 是的,我相信这是可能的,这就是为什么你应该在你的cookie标头中设置"安全"标志,这将指示浏览器不通过HTTP传输cookie,只有HTTPS.http://en.wikipedia.org/wiki/HTTP_cookie#Secure_and_HttpOnly (10认同)
  • 安全问题:假设您的应用程序使用cookie身份验证,并且登录的某人导航到站点的http:// ...部分,攻击者是否有可能在重定向发生之前嗅探他们的cookie? (2认同)

Jon*_*ran 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);
  }
});
Run Code Online (Sandbox Code Playgroud)


Eli*_*ias 1

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

编辑:更新了网址