Как заставить SSL/https в Express.js
Я пытаюсь создать промежуточное программное обеспечение для Express.js для перенаправления всего небезопасного (порт 80) трафика на защищенный порт SSL (443).
К сожалению, в запросе Express.js нет информации, которая позволяет определить, идет ли запрос через http или https.
Одним из решений было бы перенаправить запрос каждый, но это не вариант для меня.
Примечания:
Как бы вы решили это?
Ответы
Ответ 1
Поскольку я работал над nginx, у меня был доступ к свойству заголовка x-forwarded-proto, поэтому я мог написать небольшое промежуточное программное обеспечение для перенаправления всего трафика, как описано здесь: http://elias.kg/post/14971446990/force-ssl-with-express-js-on-heroku-nginx
Изменить: обновлен URL
Ответ 2
Хотя вопрос выглядит год назад, я хотел бы ответить, поскольку это может помочь другим. Его действительно просто с последней версией 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
Храните файлы сертификатов и ключей в папке, содержащей 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);
Готово!
Ответ 3
Во-первых, позвольте мне посмотреть, могу ли я прояснить проблему. Вы ограничены одним (1) node.js процессом, но этот процесс может прослушивать два (2) сетевых порта, как 80, так и 443, правильно? (Когда вы говорите один сервер, он не очищается, если вы имеете в виду один процесс или только один сетевой порт).
Учитывая это ограничение, кажется, что проблема по причинам, которые вы не предоставляете, каким-то образом ваши клиенты подключаются к неправильному порту. Это причудливый крайний случай, поскольку по умолчанию клиенты будут отправлять HTTP-запросы на порт 80 и HTTPS на порт 443. И когда я говорю "по умолчанию", я имею в виду, если в URL-адреса не указаны определенные порты. Поэтому, если вы явно не используете перекрестные URL-адреса, такие как http://example.com:443 и https://example.com:80, у вас действительно не должно быть перекрестного трафика, попавшего на ваш сайт. Но так как вы задали вопрос, я думаю, вы его должны иметь, хотя я уверен, вы используете нестандартные порты, а не по умолчанию 80/443.
Итак, для фона: ДА некоторые веб-серверы справляются с этим достаточно хорошо. Например, если вы выполните http://example.com:443 в nginx, он ответит на ответ HTTP 400 "Плохой запрос", указывающий "Простой HTTP-запрос был отправлен в порт HTTPS". ДА, вы можете прослушивать как 80, так и 443 из того же процесса node.js. Вам просто нужно создать 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 следующим образом:
$ 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-запрос, что необходимо для отправьте его через цепочку промежуточного программного обеспечения маршрута. Экспресс-стек даже не получил их, я думаю, потому что они не являются допустимыми запросами, поэтому они игнорируются где-то в стеке node 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
Ответ 4
На всякий случай, если вы размещаете на 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();
}
Вы можете использовать его с Express (2.x и 4.x) следующим образом:
app.use(requireHTTPS);
Ответ 5
На основе ответа Elias, но с встроенным кодом. Это работает, если у вас есть node за nginx или балансировщик нагрузки. Nginx или балансировщик всегда будут нажимать node на простой старый 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);
}
});
Ответ 6
http.createServer(app.handle).listen(80)
https.createServer(параметры, app.handle).listen(443)
для выражения 2x
Ответ 7
Этот код выглядит так, как будто он делает то, что вам нужно: https://gist.github.com/903596
Ответ 8
Попробуйте этот пример:
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);
});