Автоматическое соединение/перенаправление HTTPS с помощью node.js/express
Я пытаюсь настроить 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 с момента их попадания на сайт.
Как я могу получить node (и Express, поскольку это используемая инфраструктура), чтобы передать весь входящий трафик на https, независимо от того, была ли она указана или нет? Я не смог найти документацию, которая адресовала это. Или предполагается, что в рабочей среде node есть что-то, что находится перед ним (например, nginx), которое обрабатывает такой тип перенаправления?
Это мой первый набег на веб-разработку, поэтому, пожалуйста, простите мое невежество, если это что-то очевидное.
Ответы
Ответ 1
Райан, спасибо, что указал мне в правильном направлении. Я кратко сформулировал ваш ответ (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, чтобы node не запускался с правами 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
Все вместе, это работает точно так, как я этого хотел.
Ответ 2
Если вы следуете за обычными портами, поскольку 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 за один и тот же порт
Ответ 3
Спасибо этому парню: 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);
}
});
Ответ 4
С помощью 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);
}
Ответ 5
По состоянию на 0,4.12 мы не имеем реального чистого способа прослушивания HTTP и HTTPS на одном и том же порту с использованием серверов Node HTTP/HTTPS.
Некоторые люди решили эту проблему, имея Node HTTPS-сервер (это также работает с Express.js), прослушивает 443 (или какой-то другой порт), а также имеет небольшой HTTP-сервер, привязанный к 80 и перенаправляющий пользователей на безопасный порт.
Если вам абсолютно необходимо иметь возможность обрабатывать оба протокола на одном порту, вам нужно поместить nginx, lighttpd, apache или какой-либо другой веб-сервер в этот порт и действовать как обратный прокси для Node.
Ответ 6
Этот ответ необходимо обновить для работы с 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);
Ответ 7
Я нахожу req.protocol работает, когда я использую express (не тестировал без, но я подозреваю, что он работает). используя ток node 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();
}
});
Ответ 8
Если ваше приложение находится за доверенным прокси (например, AWB 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( "доверенный прокси" ) позволяет выразить проверку заголовка X-Forwarded-Proto
Ответ 9
В большинстве ответов предлагается использовать заголовок req.headers.host.
Заголовок хоста требуется HTTP 1.1, но он фактически является необязательным, поскольку HTTP-клиент не может быть отправлен на самом деле, и node/express примет этот запрос.
Вы можете спросить: какой HTTP-клиент (например, браузер) может отправить запрос, отсутствующий в этом заголовке? Протокол HTTP очень тривиален. Вы можете обрабатывать HTTP-запрос в нескольких строках кода, чтобы не отправлять заголовок узла, и если каждый раз, когда вы получаете отклоненный запрос, вы генерируете исключение, и в зависимости от того, как вы обрабатываете такие исключения, это может занять ваш сервер.
Итак всегда проверяйте все входные данные. Это не паранойя, я получил запросы, не имеющие заголовка хоста, в моем сервисе.
Кроме того, никогда не обрабатывать URL-адреса как строки. Используйте модуль URL node для изменения определенных частей строки. Обработка URL-адресов в виде строк может быть использована многими способами. Не делайте этого.
Ответ 10
Вы можете использовать модуль 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);
Ответ 11
вы можете использовать "чистый" модуль для прослушивания 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)
Ответ 12
var express = require('express');
var app = express();
app.get('*',function (req, res) {
res.redirect('https://<domain>' + req.url);
});
app.listen(80);
Это то, что мы используем, и оно отлично работает!
Ответ 13
Я использую решение, предлагаемое Basarat, но мне также необходимо перезагрузить порт, потому что у меня было два разных порта для протоколов HTTP и HTTPS.
res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url });
Я предпочитаю также использовать не стандартный порт, чтобы запустить nodejs без привилегий root.
Мне нравится 8080 и 8443 becaouse Я прихожу из многих лет программирования на 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);
Затем я использую iptable для форматирования трафика 80 и 443 на своих портах HTTP и HTTPS.
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
Ответ 14
Это сработало для меня:
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();
}
});
Ответ 15
Вы можете создать 2 сервера Node.js - один для HTTP и HTTPS
Вы также можете определить функцию настройки, которую будут выполнять оба сервера, чтобы вам не пришлось писать много дублированного кода.
Вот как я это сделал: (используя restify.js, но должен работать и для express.js, или node)
http://qugstart.com/blog/node-js/node-js-restify-server-with-both-http-and-https/
Ответ 16
Это сработало для меня:
/* Headers */
require('./security/Headers/HeadersOptions').Headers(app);
/* Server */
app.use(favicon(__dirname + '/public/favicon.ico'));
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/
Я использую экспресс 4.17.1