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

问题描述:

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

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

注:

  1. 有没有可能与Apache或别的东西来处理它。它要在节点中完成。

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

你会如何解决这个问题?

+1

可能重复的http://堆栈溢出。com/questions/7450940/automatic-https-connection-redirect-with-node-js-express – clyfe 2011-12-22 15:07:47

+0

此问题的解决方案是重定向每个请求。但就像我写的那样,这是没有选择的。 – Elias 2011-12-22 15:09:24

+0

它只重定向http的请求。 – clyfe 2011-12-22 15:13:57

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

编辑:更新了网址

+0

这个页面不见了,所以我是downvoting。如果它回来,请让我知道,所以我可以投票回来!这可以通过复制这里的一些内容来避免。谢谢。 – 2012-02-05 03:43:51

+0

感谢您的信息,我更新了网址 – Elias 2012-03-14 11:51:28

+0

非常好!改变了我的投票。 (回想起来我也许应该只是留下了评论,而不是downvoted它。) – 2012-03-14 20:02:01

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

考虑到这个约束,你的问题似乎是,由于你没有提供的原因,你的客户端连接到错误的端口。这是一个奇怪的边缘情况,因为默认情况下,客户端将向端口80发送HTTP请求,向端口443发送HTTPS。当我默认“默认”时,我的意思是如果没有特定的端口包含在URL中。因此,除非您明确使用诸如http://example.com:443https://example.com:80之类的交叉网址,否则您确实不应该在您的网站上发生任何纵横交错的流量。但是既然你问了这个问题,我想你一定有它,但我敢打赌你使用的是非标准端口而不是80/443的默认值。

所以,对于背景:是的,一些网络服务器处理这个相当好。例如,如果您对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 --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 
+2

安全问题:假设您的应用程序使用cookie身份验证,并且登录的人导航到网站的http:// ...部分,攻击者是否有可能在重定向发生之前嗅探其Cookie? – Costa 2012-06-30 21:26:27

+8

是的,我相信这是可能的,这就是为什么你应该在你的cookie头中设置“安全”标志,这将指示浏览器不要通过HTTP传输cookie,只有HTTPS。 http://en.wikipedia.org/wiki/HTTP_cookie#Secure_and_HttpOnly – 2012-06-30 22:32:49

+0

好的,太棒了!只需要弄清楚如何让CouchDB设置一个安全标志。谢谢!! – Costa 2012-07-01 13:04:14

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

https.createServer(选项,app.handle)。听(443)

特快2X

此代码看起来像它你需要:https://gist.github.com/903596

+0

我试过了,它不起作用。我得到:错误:发送后无法设置标题。之后节点进程就会死亡。 – 2012-06-05 14:52:18

虽然问题看起来岁,我想回答,因为它可能帮助别人。它最新版本的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

Store中的证书和密钥文件中包含的文件夹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 
}; 

现在通过options对象在createServer()函数

express.createServer(options); 

完成!

+1

如果明确告诉你不要叫'createServer' - http://*.com/questions/11804202/how-to-setup-a-ssl-cert-in-express-js-server-now – 2013-12-09 13:02:52

+1

链接不再作品 – ezraspectre 2014-02-25 14:04:54

基于埃利亚斯的答案,但与内联代码。如果您有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); 
    } 
}); 
+1

这样做对中间人的攻击非常脆弱。见HSTS:http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security – Qualcuno 2014-03-04 05:11:13

+1

从@乔纳森-TRAN的答案是正确的,它不是任何更容易受到男人比不这样做中间人攻击。对于那些你理想地希望与这样的事情结合使用的客户端来说,HSTS是一种额外的保护措施(尽管它在IE 11以后仍然不被IE支持)。 – 2015-05-14 13:54:28

+0

我得到这个错误从铬:ERR_TOO_MANY_REDIRECTS – 000000000000000000000 2015-09-28 11:25:47

为了防止您在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(); 
} 

你可以用快递(2.x和4.x版)像这样使用它:

app.use(requireHTTPS); 

试试这个例子:

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