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

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

一个解决scheme是redirect每个请求,但这不是我的select。

笔记:

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

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

你将如何解决?

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

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); 

完成!

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

考虑到这个限制,你的问题似乎是,由于你没有提供的原因,你的客户端连接到错误的端口。 这是一个奇怪的边缘情况,因为默认情况下,客户端将HTTP请求到端口80和HTTPS到端口443.当我说默认情况下,我的意思是如果没有特定端口包含在URL中。 所以,除非你明确地使用像http://example.com:443和https://example.com:80这样的交叉url,否则你真的不应该有任何纵横交错的stream量冲击你的网站。; 但是,既然你问了这个问题,我想你一定有这个问题,但是我敢打赌你使用的是非标准的端口,而不是80/443的默认值。

所以,对于后台:是的一些networking服务器处理这个相当不错。 例如,如果你对nginx执行了http://example.com:443 ,它将以HTTP 400的“错误请求”响应作出响应,指示“纯HTTP请求已发送到HTTPS端口”。 是的,你可以在同一个node.js进程上同时监听80和443。 你只需要创buildexpress.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来testing:

 $ 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!% 

并显示从HTTPredirect到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!% 

所以这将工作,为正常情况下的两个协议,并正确redirect。 然而,十字架根本不起作用。 我相信,一个交叉的请求打到一个快速的服务器是不会被路由通过中间件堆栈,因为它将被视为从一开始的错误,甚至不会正确parsing请求的URI,这是必要的通过路由中间件链发送它。 明确的堆栈甚至没有得到他们我认为,因为他们不是有效的请求,所以他们会被忽略节点TCP堆栈中的某个地方。 可能写一个服务器来做到这一点,并且可能已经有一个模块了,但是你必须直接写在TCP层上。 而且您必须在第一个客户端数据块中检测到一个正常的HTTP请求,这个客户端数据碰到TCP端口,并将该连接连接到HTTP服务器,而不是正常的TLS握手。

当我做这些,我的expressionerror handling程序不会被调用。

 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 

以防万一你在Heroku上托pipe,只想redirect到HTTPS而不pipe端口如何,这里是我们使用的中间件解决scheme。

如果您正在本地开发,则不需要redirect。

 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); 

根据埃利亚斯的答案,但与内联代码。 如果你有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); } }); 

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

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

为expression2x

这段代码看起来像是你所需要的: https : //gist.github.com/903596

试试这个例子:

 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); }); 

自从我在nginx上工作以来,我可以访问标头的x-forward-proto属性,所以我可以编写一个小的中间件来redirect所有的stream量,如下所述: http : //elias.kg/post/14971446990/force-ssl-与-EXPRESS-JS-上的Heroku,nginx的

编辑:更新url