Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用node.js/express自动HTTPS连接/重定向_Node.js_Https_Express - Fatal编程技术网

使用node.js/express自动HTTPS连接/重定向

使用node.js/express自动HTTPS连接/重定向,node.js,https,express,Node.js,Https,Express,我一直在尝试使用node.js项目设置HTTPS。对于本例,我基本上遵循了以下步骤: // curl -k https://localhost:8000/ var https = require('https'); var fs = require('fs'); var options = { key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), cert: fs.readFileSync('test/fixtures/k

我一直在尝试使用node.js项目设置HTTPS。对于本例,我基本上遵循了以下步骤:

// curl -k https://localhost:8000/
var https = require('https');
var fs = require('fs');

var options = {
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world\n");
}).listen(8000);
现在,当我

curl -k https://localhost:8000/
我明白了

hello world
curl: (52) Empty reply from server
正如所料。但如果我这样做了

curl -k http://localhost:8000/
我明白了

hello world
curl: (52) Empty reply from server
回想起来,这似乎是显而易见的,它将以这种方式工作,但同时,最终访问我的项目的人不会键入https://yadayada,我希望从他们访问网站的那一刻起,所有流量都是https

我如何让node(以及我正在使用的框架)将所有传入的流量传递给https,而不管是否指定了它?我还没有找到任何解决这个问题的文档。或者只是假设在生产环境中,节点前面有处理这种重定向的东西(例如nginx)


这是我第一次涉足web开发,如果这是显而易见的,请原谅我的无知。

从0.4.12开始,我们没有真正干净的方法使用Node的HTTP/HTTPS服务器在同一端口上侦听HTTP和HTTPS

有些人通过让Node的HTTPS服务器(这也适用于Express.js)监听443(或其他端口),并让一个小型http服务器绑定到80并将用户重定向到安全端口,从而解决了这个问题


如果您必须能够在一个端口上处理这两个协议,那么您需要在该端口上安装nginx、lighttpd、apache或其他一些web服务器,并充当节点的反向代理。

Ryan,感谢您为我指明了正确的方向。我用一些代码把你的答案(第二段)充实了一点,效果很好。在本场景中,这些代码片段放在my express应用程序中:

// set up plain http server
var http = express();

// set up a route to redirect http to https
http.get('*', function(req, res) {  
    res.redirect('https://' + req.headers.host + req.url);

    // Or, if you don't want to automatically detect the domain name from the request header, you can hard code it:
    // res.redirect('https://example.com' + req.url);
})

// have it listen on 8080
http.listen(8080);
https express服务器在3000上侦听ATM。我设置了这些iptables规则,这样节点就不必以root用户身份运行:

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 3000
总之,这完全符合我的要求

要防止通过HTTP窃取Cookie,请参阅(注释)或使用以下代码:

const session=require('cookie-session');
应用程序使用(
会议({
秘密:“一些秘密”,
httpOnly:true,//不要让浏览器javascript访问cookie。
secure:true,//仅通过https使用cookie。
})
);

您可以实例化2个Node.js服务器—一个用于HTTP和HTTPS

您还可以定义两台服务器都将执行的设置函数,这样就不必编写太多重复的代码

我是这样做的:(使用restify.js,但应该适用于express.js或节点本身)


使用Nginx,您可以利用“x-forwarded-proto”标题:

您可以使用“net”模块在同一端口上侦听HTTP和HTTPS

var https = require('https');
var http = require('http');
var fs = require('fs');

var net=require('net');
var handle=net.createServer().listen(8000)

var options = {
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world\n");
}).listen(handle);

http.createServer(function(req,res){
  res.writeHead(200);
  res.end("hello world\n");
}).listen(handle)

当我使用express时,我发现req.protocol可以工作(没有测试过,但我怀疑它可以工作)。在express 3.4.3中使用当前节点0.10.22

app.use(function(req,res,next) {
  if (!/https/.test(req.protocol)){
     res.redirect("https://" + req.headers.host + req.url);
  } else {
     return next();
  } 
});

此答案需要更新才能使用Express 4.0。下面是我如何让单独的http服务器工作的:

var express = require('express');
var http = require('http');
var https = require('https');

// Primary https app
var app = express()
var port = process.env.PORT || 3000;
app.set('env', 'development');
app.set('port', port);
var router = express.Router();
app.use('/', router);
// ... other routes here
var certOpts = {
    key: '/path/to/key.pem',
    cert: '/path/to/cert.pem'
};
var server = https.createServer(certOpts, app);
server.listen(port, function(){
    console.log('Express server listening to port '+port);
});


// Secondary http app
var httpApp = express();
var httpRouter = express.Router();
httpApp.use('*', httpRouter);
httpRouter.get('*', function(req, res){
    var host = req.get('Host');
    // replace the port in the host
    host = host.replace(/:\d+$/, ":"+app.get('port'));
    // determine the redirect destination
    var destination = ['https://', host, req.url].join('');
    return res.redirect(destination);
});
var httpServer = http.createServer(httpApp);
httpServer.listen(8080);

如果您遵循传统端口,因为默认情况下HTTP尝试端口80,默认情况下HTTPS尝试端口443,那么您可以在同一台机器上拥有两台服务器: 代码如下:

var https = require('https');

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

https.createServer(options, function (req, res) {
    res.end('secure!');
}).listen(443);

// Redirect from http port 80 to https
var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url });
    res.end();
}).listen(80);
使用https进行测试:

$ curl https://127.0.0.1 -k
secure!
使用http:

$ curl http://127.0.0.1 -i
HTTP/1.1 301 Moved Permanently
Location: https://127.0.0.1/
Date: Sun, 01 Jun 2014 06:15:16 GMT
Connection: keep-alive
Transfer-Encoding: chunked
更多详细信息:

var express=require('express');
var-app=express();
app.get('*',函数(req,res){
res.redirect('https://'+req.url);
});
app.listen(80);

这就是我们所使用的,它工作得很好

这里的大多数答案建议使用req.headers.host头

HTTP 1.1需要主机头,但它实际上是可选的,因为头可能不是由HTTP客户机实际发送的,node/express将接受此请求

您可能会问:哪个HTTP客户端(例如:浏览器)可以发送缺少该头的请求?HTTP协议非常简单。您可以在几行代码中创建一个HTTP请求,以不发送主机头,如果每次收到格式错误的请求时都抛出一个异常,并且取决于您处理此类异常的方式,这可能会导致服务器停机

因此,始终验证所有输入。这不是妄想症,我收到的请求在我的服务中缺少主机头


另外,从不将URL视为字符串。使用节点url模块修改字符串的特定部分。将URL视为字符串可以通过多种方式加以利用。不要这样做。

如果您的应用程序位于受信任的代理(例如AWS ELB或正确配置的nginx)之后,则此代码应该可以工作:

app.enable('trust proxy');
app.use(function(req, res, next) {
    if (req.secure){
        return next();
    }
    res.redirect("https://" + req.headers.host + req.url);
});
注:

  • 这假设您的站点位于80和443上,如果不是,则需要在重定向时更改端口
  • 这还假设您正在终止代理上的SSL。如果您正在执行SSL端到端,请使用上面@basarat的答案。端到端SSL是更好的解决方案
  • app.enable('trust proxy')允许express检查X-Forwarded-Proto标头
    • 这对我来说很有效:

      app.use(function(req,res,next) {
          if(req.headers["x-forwarded-proto"] == "http") {
              res.redirect("https://[your url goes here]" + req.url, next);
          } else {
              return next();
          } 
      });
      
      /* Headers */
      require('./security/Headers/HeadersOptions').Headers(app);
      
      /* Server */
      const ssl = {
          key: fs.readFileSync('security/ssl/cert.key'),
          cert: fs.readFileSync('security/ssl/cert.pem')
      };
      //https server
      https.createServer(ssl, app).listen(443, '192.168.1.2' && 443, '127.0.0.1');
      //http server
      app.listen(80, '192.168.1.2' && 80, '127.0.0.1');
      app.use(function(req, res, next) {
          if(req.secure){
              next();
          }else{
              res.redirect('https://' + req.headers.host + req.url);
          }
      });
      

      我使用Basarat提出的解决方案,但我还需要覆盖端口,因为我以前有两个不同的HTTP和HTTPS协议端口

      res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url });
      
      我更喜欢使用非标准端口,以便在没有root权限的情况下启动nodejs。 我喜欢8080和8443,因为我在tomcat上编程多年

      我的完整文件成为

      var fs = require('fs');
      var http = require('http');
      var http_port    =   process.env.PORT || 8080; 
      var app = require('express')();
      
      // HTTPS definitions
      var https = require('https');
      var https_port    =   process.env.PORT_HTTPS || 8443; 
      var options = {
         key  : fs.readFileSync('server.key'),
         cert : fs.readFileSync('server.crt')
      };
      
      app.get('/', function (req, res) {
         res.send('Hello World!');
      });
      
      https.createServer(options, app).listen(https_port, function () {
         console.log('Magic happens on port ' + https_port); 
      });
      
      // Redirect from http port to https
      http.createServer(function (req, res) {
          res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url });
          console.log("http request, will go to >> ");
          console.log("https://" + req.headers['host'].replace(http_port,https_port) + req.url );
          res.end();
      }).listen(http_port);
      
      然后我在HTTP和HTTPS端口上使用iptable来处理80和443流量

      sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
      sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8443
      
      您可以使用以下模块:

      npm安装--保存express force https

      var express=require('express');
      var secure=require('express-force-https');
      var-app=express();
      应用程序使用(安全);
      
      多亏了这个家伙:
      

      如果安全,则通过https请求,否则重定向到https

      app.enable('trust proxy')
      app.use((req, res, next) => {
          req.secure ? next() : res.redirect('https://' + req.headers.host + req.url)
      })
      
      这对我很有用:

      app.use(function(req,res,next) {
          if(req.headers["x-forwarded-proto"] == "http") {
              res.redirect("https://[your url goes here]" + req.url, next);
          } else {
              return next();
          } 
      });
      
      /* Headers */
      require('./security/Headers/HeadersOptions').Headers(app);
      
      /* Server */
      const ssl = {
          key: fs.readFileSync('security/ssl/cert.key'),
          cert: fs.readFileSync('security/ssl/cert.pem')
      };
      //https server
      https.createServer(ssl, app).listen(443, '192.168.1.2' && 443, '127.0.0.1');
      //http server
      app.listen(80, '192.168.1.2' && 80, '127.0.0.1');
      app.use(function(req, res, next) {
          if(req.secure){
              next();
          }else{
              res.redirect('https://' + req.headers.host + req.url);
          }
      });
      
      建议在重定向到https之前添加标题

      现在,当您这样做时:

      curl http://127.0.0.1 --include
      
      你会得到:

      HTTP/1.1 302 Found
      //
      Location: https://127.0.0.1/
      Vary: Accept
      Content-Type: text/plain; charset=utf-8
      Content-Length: 40
      Date: Thu, 04 Jul 2019 09:57:34 GMT
      Connection: keep-alive
      
      Found. Redirecting to https://127.0.0.1/
      

      如果您的节点应用程序安装在
      (function(){
        var link = window.location.href;
        if(link[4] != "s"){
          var clink = "";
          for (let i = 4; i < link.length; i++) {
            clink += link[i];
          }
          window.location.href = "https" + clink;
        }
      })();
      
      app.use (function (req, res, next) {
        if (req.secure) {
                next();
        } else {
                res.redirect('https://' + req.headers.host + req.url);
        }
      });