Автоматическое HTTPS-соединение / перенаправление с помощью node.js / express - PullRequest
148 голосов
/ 17 сентября 2011

Я пытался настроить HTTPS с проектом node.js, над которым я работаю. Я по существу следовал документации node.js для этого примера:

// 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 -k http://localhost:8000/

Я получаю

curl: (52) Empty reply from server

В ретроспективе кажется очевидным, что это будет работать таким образом, но в то же время люди, которые в конечном итоге посещают мой проект, не собираются вводить https : // yadayada, и я хочу, чтобы все трафик будет https с момента их попадания на сайт.

Как я могу заставить узел (и Express, так как это среда, которую я использую) передавать весь входящий трафик на https, независимо от того, был ли он указан? Я не смог найти никаких документов, которые касались этого. Или просто предполагается, что в производственной среде узел имеет что-то, что находится перед ним (например, nginx), который обрабатывает такой тип перенаправления?

Это мой первый опыт веб-разработки, поэтому, пожалуйста, прости мое невежество, если это что-то очевидное.

Ответы [ 16 ]

153 голосов
/ 18 сентября 2011

Райан, спасибо, что указал мне правильное направление. Я немного уточнил ваш ответ (2-й абзац) с помощью некоторого кода, и он работает. В этом сценарии эти фрагменты кода помещаются в мое экспресс-приложение:

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

// 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 прослушивает ATM на 3000. Я установил эти правила 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

Все вместе, это работает именно так, как я хотел.

108 голосов
/ 01 июня 2014

Если вы используете обычные порты, так как 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

Подробнее: Nodejs HTTP и HTTPS через один и тот же порт

61 голосов
/ 08 марта 2018

Спасибо этому парню: https://www.tonyerwin.com/2014/09/redirecting-http-to-https-with-nodejs.html

app.use (function (req, res, next) {
        if (req.secure) {
                // request was via https, so do no special handling
                next();
        } else {
                // request was via http, so redirect to https
                res.redirect('https://' + req.headers.host + req.url);
        }
});
22 голосов
/ 01 мая 2012

С Nginx вы можете воспользоваться заголовком "x-forwarded-proto":

function ensureSec(req, res, next){
    if (req.headers["x-forwarded-proto"] === "https"){
       return next();
    }
    res.redirect("https://" + req.headers.host + req.url);  
}
12 голосов
/ 17 сентября 2011

По состоянию на 0.4.12 у нас нет реального чистого способа прослушивания HTTP и HTTPS на одном и том же порту с использованием серверов Node HTTP / HTTPS.

Некоторые люди решили эту проблему, имея HTTPS-сервер Node (который также работает с Express.js), слушая 443 (или какой-либо другой порт), а также привязывая небольшой http-сервер к 80 и перенаправляя пользователей на защищенный порт.

Если вам абсолютно необходимо иметь возможность обрабатывать оба протокола на одном порту, тогда вам нужно установить nginx, lighttpd, apache или какой-либо другой веб-сервер на этот порт и действовать как обратный прокси-сервер для Node.

8 голосов
/ 25 апреля 2014

Этот ответ необходимо обновить для работы с 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);
6 голосов
/ 22 ноября 2013

Я считаю, что req.protocol работает, когда я использую экспресс (не тестировал без, но я подозреваю, что он работает). используя текущий узел 0.10.22 с экспрессом 3.4.3

app.use(function(req,res,next) {
  if (!/https/.test(req.protocol)){
     res.redirect("https://" + req.headers.host + req.url);
  } else {
     return next();
  } 
});
5 голосов
/ 09 октября 2016

Если ваше приложение находится за доверенным прокси-сервером (например, 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 («доверенный прокси») позволяет Express проверять заголовок X-Forwarded-Proto
4 голосов
/ 22 февраля 2018

Вы можете использовать модуль express-force-https :

npm install --save express-force-https

var express = require('express');
var secure = require('express-force-https');

var app = express();
app.use(secure);
4 голосов
/ 28 июля 2016

Большинство ответов здесь предлагают использовать заголовок req.headers.host.

Заголовок Host требуется HTTP 1.1, но на самом деле он необязательный, поскольку заголовок может фактически не отправляться HTTP-клиентом,и узел / экспресс примет этот запрос.

Вы можете спросить: какой HTTP-клиент (например, браузер) может отправить запрос без этого заголовка?Протокол HTTP очень тривиален.Вы можете создать HTTP-запрос в несколько строк кода, чтобы не отправлять заголовок узла, и если каждый раз, когда вы получаете неверно сформированный запрос, вы генерируете исключение, и в зависимости от того, как вы обрабатываете такие исключения, это может привести к отключению сервера.1005 *

То есть всегда проверяет все входные данные .Это не паранойя, я получил запросы без заголовка узла в моем сервисе.

Кроме того, никогда не рассматривает URL как строки .Используйте модуль url узла, чтобы изменить определенные части строки.Обработка URL-адресов как строк может быть использована многими многими способами.Не делай этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...