Heroku NodeJS wymuszone przekierowanie z http na https ssl

105

Mam uruchomioną aplikację na heroku z ekspresem na węźle z https ,. Jak zidentyfikować protokół wymuszający przekierowanie na https z nodejs na heroku?

Moja aplikacja to zwykły serwer http, nie zdaje sobie (jeszcze) że Heroku wysyła do niej żądania https:

/* Heroku provides the port they want you on in this environment variable (hint: it's not 80) */
app.listen(process.env.PORT || 3000);
Derek Bredensteiner
źródło
6
Zespół pomocy Heroku odpowiedział na moje powyższe pytanie i nie znalazłem go już tutaj opublikowanego, więc pomyślałem, że opublikuję go publicznie i podzielę się wiedzą. Przekazują wiele informacji o pierwotnym żądaniu z jego nagłówkami z prefiksem „x-”. Oto kod, którego teraz używam (u góry moich definicji tras):app.get('*',function(req,res,next){ if(req.headers['x-forwarded-proto']!='https') res.redirect('https://mypreferreddomain.com'+req.url) else next() })
Derek Bredensteiner.
1
ok, więc rozumiem, że sprawdzasz https w ten sposób i przekierowujesz w razie potrzeby. Ale czy istnieje sposób na przekierowanie na poziomie dns z dostawcą nazwy domeny. Więc zanim przeglądarka rozwiąże DNS, jest już pod adresem https. Ponieważ przy takim podejściu myślę, biorąc pod uwagę moją wiedzę na temat przekierowań, że raz żądanie jest wysyłane przez http, a następnie ponownie przez https. Więc jeśli przesyłane były dane wrażliwe, były one wysyłane raz przez http. następnie przez https. Co w pewnym sensie przeczy celowi. Daj mi znać, jeśli się mylę.
Muhammad Umer
@MuhammadUmer, twoje rozumowanie wydaje się być na miejscu, czy kiedykolwiek odkryłeś więcej?
Karoh
Po prostu użyłem cloudflare jako serwera nazw, który działa jako nginx i pozwala mi przekierować do wersji ssl po prostu klikając przycisk przełączania. również możesz to zrobić: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/… Ponadto zazwyczaj nikt nie wysyła danych od razu, zwykle lądują one na formularzu, a następnie przesyłają. więc w kodzie po stronie serwera, serwerze dns, nagłówku http, javascript można sprawdzić i przekierować do https developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
Muhammad Umer

Odpowiedzi:

107

Na dzień dzisiejszy, 10 października 2014 r. , Przy użyciu stosu Heroku Cedar i ExpressJS ~ 3.4.4 , mamy działający zestaw kodu.

Najważniejszą rzeczą do zapamiętania jest to, że wdrażamy się do Heroku. Zakończenie SSL następuje w module równoważenia obciążenia, zanim zaszyfrowany ruch dotrze do aplikacji węzła. Możliwe jest sprawdzenie, czy do wysłania żądania został użyty protokół https za pomocą req.headers ['x-forwarded-proto'] === 'https' .

Nie musimy martwić się posiadaniem lokalnych certyfikatów SSL w aplikacji itp., Jak w przypadku hostingu w innych środowiskach. Jednak jeśli używasz własnego certyfikatu, subdomen itp., Powinieneś najpierw uzyskać dodatek SSL zastosowany za pośrednictwem dodatków Heroku.

Następnie po prostu dodaj poniższe, aby wykonać przekierowanie z czegokolwiek innego niż HTTPS do HTTPS. Jest to bardzo zbliżone do zaakceptowanej odpowiedzi powyżej, ale:

  1. Zapewnia, że ​​używasz „app.use” (do wszystkich działań, a nie tylko do pobierania)
  2. Jawnie uzewnętrznia logikę forceSsl do zadeklarowanej funkcji
  3. Nie używa znaku „*” z „app.use” - to faktycznie zawiodło, kiedy go testowałem.
  4. Tutaj chcę tylko SSL w produkcji. (Zmień zgodnie z własnymi potrzebami)

Kod:

 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...
}

Uwaga dla użytkowników SailsJS (0.10.x). Możesz po prostu utworzyć politykę (egzekwowanieSsl.js) wewnątrz interfejsu API / polityk:

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();
  }
};

Następnie odwołaj się do pliku config / policies.js wraz z innymi zasadami, np .:

„*”: [„uwierzytelniony”, „wymuszonySsl”]

arcseldon
źródło
1
Uwaga dotycząca korzystania z zasad żagli: Jak podano w sailsjs.org/#/documentation/concepts/Policies : „Domyślne mapowania zasad nie są„ kaskadowe ”ani„ spływają ”. Określone mapowania działań kontrolera zastąpią domyślne mapowanie. " Oznacza to, że gdy tylko będziesz mieć inne zasady dla określonego kontrolera / akcji, będziesz musiał dodać „wymuszanieSsl” do tych kontrolerów / akcji.
Manuel Darveau,
2
W poniższej tabeli wymieniono inne małe, ale ważne zmiany w Express 4: ... Funkcja app.configure () została usunięta. Użyj funkcji process.env.NODE_ENV lub app.get ('env'), aby wykryć środowisko i odpowiednio skonfiguruj aplikację. ”
Kevin Wheeler,
9
Zauważ też, że res.redirectdomyślnie jest to przekierowanie 302 (przynajmniej w express 4.x). Ze względów SEO i buforowania prawdopodobnie zamiast tego potrzebujesz przekierowania 301. Zamień odpowiednią linię nareturn res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
Kevin Wheeler
6
Uwaga: w Express 4.x, usuń app.configurelinię i po prostu użyj wewnętrznej mikstury. app.configurejest starszym kodem i nie jest już uwzględniony w usłudze Express
Augie Gardner
96

Odpowiedzią jest użycie nagłówka „x-forwarded-proto”, który Heroku przekazuje dalej, ponieważ jest to proxy thingamabob. (uwaga na marginesie: przekazują również kilka innych zmiennych x, które mogą być przydatne, sprawdź je ).

Mój kod:

/* 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 */
})

Dzięki Brandon, właśnie czekałem na to 6-godzinne opóźnienie, które nie pozwoliło mi odpowiedzieć na moje własne pytanie.

Derek Bredensteiner
źródło
4
czy nie pozwoliłoby to na inne metody niż GETprzez?
Jed Schmidt
1
@Aaron: Cóż, potencjalnie straciłbyś informacje, gdybyś w sposób przejrzysty przekierował żądanie POST. Myślę, że powinieneś zwrócić 400 w przypadku innych żądań niż GET dla http.
theodorton
3
Możesz wrzucić && process.env.NODE_ENV === "production"do swojego warunku warunkowego, jeśli chcesz, aby działał tylko w środowisku produkcyjnym.
keepitreal
307 (przekierowanie tą samą metodą) jest prawdopodobnie lepsze niż błąd 400.
Beni Cherniavsky-Paskin
Jest wiele problemów z tą odpowiedzią, zobacz następną odpowiedź poniżej ( stackoverflow.com/a/23894573/14193 ) i oceń ją.
Neil
22

Zaakceptowana odpowiedź zawiera zakodowaną na stałe domenę, co nie jest zbyt dobre, jeśli masz ten sam kod w kilku domenach (np .: dev-yourapp.com, test-yourapp.com, yourapp.com).

Użyj tego zamiast tego:

/* 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/

Joan-Diego Rodriguez
źródło
Działa ładnie. I Duno dlaczego po prostu musiałem wymienić req.hostnamez req.headers.hostwersji może wyraźnej Jestem w 4.2
Jeremy Piednoel
16

Napisałem mały moduł węzłowy, który wymusza SSL w projektach ekspresowych. Działa zarówno w standardowych sytuacjach, jak iw przypadku odwrotnych proxy (Heroku, nodejitsu itp.)

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

florian
źródło
6

Jeśli chcesz przetestować x-forwarded-protonagłówek na swoim hoście lokalnym, możesz użyć nginx do skonfigurowania pliku vhost, który przekazuje wszystkie żądania do aplikacji węzła. Twój plik konfiguracyjny vhost nginx może wyglądać tak

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;
  }
}

Ważne jest tutaj to, że przekazujesz wszystkie żądania do portu localhost 3000 (to jest miejsce, w którym działa twoja aplikacja węzła) i konfigurujesz kilka nagłówków, w tym X-Forwarded-Proto

Następnie w aplikacji wykryj ten nagłówek jak zwykle

Wyrazić

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()
  })

Koa

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
})

Zastępy niebieskie

Na koniec musisz dodać tę linię do swojego hostspliku

127.0.0.1 dummy.com
simo
źródło
6

Powinieneś przyjrzeć się przekierowaniu heroku-ssl- . To działa jak urok!

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);
Julien Le Coupanec
źródło
4

Jeśli używasz cloudflare.com jako CDN w połączeniu z heroku, możesz łatwo włączyć automatyczne przekierowanie ssl w cloudflare w następujący sposób:

  1. Zaloguj się i przejdź do swojego panelu

  2. Wybierz Reguły strony

    Wybierz Reguły strony

  3. Dodaj swoją domenę, np. Www.example.com i przełącz zawsze używaj https na on Włącz opcję Zawsze używaj protokołu HTTPS
electronix384128
źródło
3

Użytkownicy Loopback mogą używać nieco dostosowanej wersji odpowiedzi arcseldon jako oprogramowania pośredniego:

serwer / oprogramowanie pośredniczące / 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 / server.js

var forceSSL = require('./middleware/forcessl.js');
app.use(forceSSL());
Bunkier
źródło
2

Jest to bardziej wyraźny sposób, aby to zrobić.

app.enable('trust proxy');
app.use('*', (req, res, next) => {
  if (req.secure) {
    return next();
  }
  res.redirect(`https://${req.hostname}${req.url}`);
});
denixtry
źródło
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
źródło
0

Dzięki app.use i dynamicznemu adresowi URL. Dla mnie działa zarówno lokalnie, jak i na Heroku

app.use(function (req, res, next) {
  if (req.header('x-forwarded-proto') === 'http') {
    res.redirect(301, 'https://' + req.hostname + req.url);
    return
  }
  next()
});
tuancharlie
źródło
-1

Sprawdzanie protokołu w nagłówku X-Forwarded-Proto działa dobrze na Heroku, tak jak wskazał Derek. Na co warto, tu jest sedno z middleware wyrazić, że używam i jego odpowiedniego testu.

Peter Marklund
źródło