Heroku NodeJS http на https ssl принудительное перенаправление

105

У меня есть приложение, работающее на heroku с экспрессом на узле с https. Как мне определить протокол для принудительного перенаправления на https с помощью nodejs на heroku?

Мое приложение - это простой http-сервер, он (пока) не понимает, что heroku отправляет ему https-запросы:

/* Heroku provides the port they want you on in this environment variable (hint: it's not 80) */
app.listen(process.env.PORT || 3000);
Дерек Бреденштайнер
источник
6
Служба поддержки Heroku ответила на мой вышеупомянутый вопрос, и я не нашел его здесь, поэтому я подумал, что выложу его публично и поделюсь знаниями. Они передают много информации об исходном запросе с его заголовками с префиксом «x-». Вот код, который я использую сейчас (в верхней части моих определений маршрута):app.get('*',function(req,res,next){ if(req.headers['x-forwarded-proto']!='https') res.redirect('https://mypreferreddomain.com'+req.url) else next() })
Дерек Бреденштайнер
1
Хорошо, я понимаю, что вы проверяете https, как это, и при необходимости перенаправляете. Но есть ли способ сделать перенаправление на уровне DNS с вашим провайдером доменного имени. Итак, прежде чем браузер разрешит DNS, он уже находится на https. Поскольку при таком подходе, я думаю, учитывая мои знания о перенаправлениях, один раз запрос выполняется через http, а затем снова через https. Таким образом, если конфиденциальные данные были отправлены, они были отправлены через http один раз. затем по https. Что в некотором роде противоречит цели. Пожалуйста, дайте мне знать, если я ошибаюсь.
Мухаммад Умер
@MuhammadUmer, ваши рассуждения здесь кажутся точными, вы когда-нибудь открывали для себя больше?
Karoh
Я просто использовал cloudflare в качестве сервера имен, который работает как nginx и позволяет мне перенаправить на версию ssl, просто нажав кнопку переключения. также вы можете сделать это: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/… Более того, обычно никто не отправляет данные сразу, они обычно попадают в форму, а затем отправляют. поэтому код на стороне сервера, DNS-сервер, http-заголовок, javascript вы можете проверить и перенаправить на https developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
Мухаммад Умер

Ответы:

107

На сегодняшний день, 10 октября 2014 г. , с использованием стека Heroku Cedar и ExpressJS ~ 3.4.4 , вот рабочий набор кода.

Главное, что нужно помнить, это то, что мы развертываем на Heroku. Прекращение действия SSL происходит на балансировщике нагрузки, прежде чем зашифрованный трафик достигнет вашего приложения узла. Можно проверить, использовался ли https для выполнения запроса с помощью req.headers ['x-forwarded-proto'] === 'https' .

Нам не нужно беспокоиться о наличии локальных SSL-сертификатов внутри приложения и т. Д., Как если бы вы размещали в других средах. Однако вы должны сначала получить надстройку SSL, применяемую через надстройки Heroku, если используете свой собственный сертификат, поддомены и т. Д.

Затем просто добавьте следующее, чтобы выполнить перенаправление с чего-либо, кроме HTTPS, на HTTPS. Это очень близко к принятому выше ответу, но:

  1. Гарантирует, что вы используете app.use (для всех действий, а не только для получения)
  2. Явно переносит логику forceSsl в объявленную функцию
  3. Не использует "*" с "app.use" - это действительно не удалось, когда я его тестировал.
  4. Здесь мне нужен только SSL в производстве. (Измените по своему усмотрению)

Код:

 var express = require('express'),
   env = process.env.NODE_ENV || 'development';

 var forceSsl = function (req, res, next) {
    if (req.headers['x-forwarded-proto'] !== 'https') {
        return res.redirect(['https://', req.get('Host'), req.url].join(''));
    }
    return next();
 };

 app.configure(function () {

    if (env === 'production') {
        app.use(forceSsl);
    }

    // other configurations etc for express go here...
}

Примечание для пользователей SailsJS (0.10.x). Вы можете просто создать политику (enforceSsl.js) внутри api / policy:

module.exports = function (req, res, next) {
  'use strict';
  if ((req.headers['x-forwarded-proto'] !== 'https') && (process.env.NODE_ENV === 'production')) {
    return res.redirect([
      'https://',
      req.get('Host'),
      req.url
    ].join(''));
  } else {
    next();
  }
};

Затем обратитесь к config / policy.js вместе с любыми другими политиками, например:

'*': ['аутентифицирован', 'enforceSsl']

Arcseldon
источник
1
Примечание об использовании политики парусов: Как указано в sailsjs.org/#/documentation/concepts/Policies : «Отображения политик по умолчанию не« каскадируются »или« просачиваются вниз ». Указанные сопоставления для действий контроллера переопределяют сопоставление по умолчанию. " Это означает, что как только у вас появятся другие политики для конкретного контроллера / действия, вам нужно будет обязательно добавить enforceSsl для этого контроллера / действия.
Мануэль Дарво
2
"В следующей таблице перечислены другие небольшие, но важные изменения в Express 4: ... Функция app.configure () была удалена. Используйте функцию process.env.NODE_ENV или app.get ('env'), чтобы определить среду и настройте приложение соответствующим образом »,
Кевин Уиллер,
9
Также обратите внимание, что по res.redirectумолчанию используется перенаправление 302 (по крайней мере, в Express 4.x). Из соображений SEO и кеширования вам, вероятно, потребуется перенаправление 301. Заменить соответствующую строку наreturn res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
Kevin Wheeler
6
Примечание: Express 4.xудалите app.configureлинию и просто используйте внутреннее зелье. app.configureявляется устаревшим кодом и больше не входит в экспресс.
Оги Гарднер
96

Ответ - использовать заголовок «x-forwarded-proto», который Heroku передает вперед, как это делает его прокси-объект. (примечание: они также передают несколько других x-переменных, которые могут быть удобными, проверьте их ).

Мой код:

/* At the top, with other redirect methods before other routes */
app.get('*',function(req,res,next){
  if(req.headers['x-forwarded-proto']!='https')
    res.redirect('https://mypreferreddomain.com'+req.url)
  else
    next() /* Continue to other routes if we're not redirecting */
})

Спасибо, Брэндон, просто ждал 6-часовой задержки, которая не позволила мне ответить на мой собственный вопрос.

Дерек Бреденштайнер
источник
4
разве это не пропустит другие методы, кроме как GETчерез?
Джед Шмидт
1
@Aaron: Ну, вы потенциально потеряете информацию, если прозрачно перенаправите POST-запрос. Я думаю, вы должны возвращать 400 по другим запросам, кроме GET для http.
theodorton
3
Вы можете добавить && process.env.NODE_ENV === "production"в свое условие, если хотите, чтобы он работал только в производственной среде.
keepitreal 08
307 (перенаправление тем же методом), вероятно, лучше, чем ошибка 400.
Бени Чернявский-Паскин
С этим ответом связано несколько проблем, см. Следующий ответ ниже ( stackoverflow.com/a/23894573/14193 ) и оцените его ниже.
Нил
22

В принятом ответе есть жестко запрограммированный домен, что не очень хорошо, если у вас есть один и тот же код на нескольких доменах (например, dev-yourapp.com, test-yourapp.com, yourapp.com).

Используйте вместо этого:

/* Redirect http to https */
app.get('*', function(req,res,next) {
  if(req.headers['x-forwarded-proto'] != 'https' && process.env.NODE_ENV === 'production')
    res.redirect('https://'+req.hostname+req.url)
  else
    next() /* Continue to other routes if we're not redirecting */
});

https://blog.mako.ai/2016/03/30/redirect-http-to-https-on-heroku-and-node-generally/

Жоан-Диего Родригес
источник
Хорошо работает. Я Дуно , почему я просто должен был заменить req.hostnameс , req.headers.hostвозможно , экспресс - версии я нахожусь в 4,2
Джереми Piednoel
16

Я написал небольшой модуль узла, который применяет SSL в экспресс-проектах. Работает как в стандартных ситуациях, так и в случае обратных прокси (Heroku, nodejitsu и др.)

https://github.com/florianheinemann/express-sslify

Флорианец
источник
6

Если вы хотите протестировать x-forwarded-protoзаголовок на своем локальном хосте, вы можете использовать nginx для настройки файла vhost, который проксирует все запросы к вашему приложению node. Ваш файл конфигурации nginx vhost может выглядеть так

NginX

server {
  listen 80;
  listen 443;

  server_name dummy.com;

  ssl on;
  ssl_certificate     /absolute/path/to/public.pem;
  ssl_certificate_key /absolute/path/to/private.pem;

  access_log /var/log/nginx/dummy-access.log;
  error_log /var/log/nginx/dummy-error.log debug;

  # node
  location / {
    proxy_pass http://127.0.0.1:3000/;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

Важным моментом здесь является то, что вы проксируете все запросы на порт 3000 localhost (именно здесь работает ваше приложение узла) и настраиваете кучу заголовков, включая X-Forwarded-Proto

Затем в вашем приложении определите этот заголовок как обычно

Экспресс

var app = express()
  .use(function (req, res, next) {
    if (req.header('x-forwarded-proto') == 'http') {
      res.redirect(301, 'https://' + 'dummy.com' + req.url)
      return
    }
    next()
  })

Коа

var app = koa()
app.use(function* (next) {
  if (this.request.headers['x-forwarded-proto'] == 'http') {
    this.response.redirect('https://' + 'dummy.com' + this.request.url)
    return
  }
  yield next
})

Хосты

Наконец, вам нужно добавить эту строку в свой hostsфайл

127.0.0.1 dummy.com
симо
источник
6

Вам следует взглянуть на heroku-ssl-redirect . Работает как часы!

var sslRedirect = require('heroku-ssl-redirect');
var express = require('express');
var app = express();

// enable ssl redirect
app.use(sslRedirect());

app.get('/', function(req, res){
  res.send('hello world');
});

app.listen(3000);
Жюльен Ле Купанек
источник
4

Если вы используете cloudflare.com в качестве CDN в сочетании с heroku, вы можете легко включить автоматическое перенаправление ssl в cloudflare следующим образом:

  1. Войдите и перейдите в личный кабинет

  2. Выберите правила страницы

    Выберите правила страницы

  3. Добавьте свой домен, например www.example.com, и включите всегда использовать https. Включите всегда использовать https
электроникс384128
источник
3

Пользователи Loopback могут использовать слегка адаптированную версию ответа arcseldon в качестве промежуточного программного обеспечения:

сервер / промежуточное ПО / forcessl.js

module.exports = function() {  
  return function forceSSL(req, res, next) {
    var FORCE_HTTPS = process.env.FORCE_HTTPS || false;
      if (req.headers['x-forwarded-proto'] !== 'https' && FORCE_HTTPS) {
        return res.redirect(['https://', req.get('Host'), req.url].join(''));
      }
      next();
    };
 };

сервер / server.js

var forceSSL = require('./middleware/forcessl.js');
app.use(forceSSL());
Бункер
источник
2

Это более конкретный способ Express.

app.enable('trust proxy');
app.use('*', (req, res, next) => {
  if (req.secure) {
    return next();
  }
  res.redirect(`https://${req.hostname}${req.url}`);
});
Denixtry
источник
0
app.all('*',function(req,res,next){
  if(req.headers['x-forwarded-proto']!='https') {
    res.redirect(`https://${req.get('host')}`+req.url);
  } else {
    next(); /* Continue to other routes if we're not redirecting */
  }
});
Chiedo
источник
0

С app.use и динамическим URL. Работает как локально, так и на Heroku у меня

app.use(function (req, res, next) {
  if (req.header('x-forwarded-proto') === 'http') {
    res.redirect(301, 'https://' + req.hostname + req.url);
    return
  }
  next()
});
Туанчарли
источник
-1

Проверка протокола в заголовке X-Forwarded-Proto отлично работает на Heroku, как и указал Дерек. Как бы то ни было, вот суть промежуточного программного обеспечения Express, которое я использую, и его соответствующий тест.

Питер Марклунд
источник