Heroku NodeJS da http a https ssl reindirizzamento forzato


105

Ho un'applicazione attiva e funzionante su heroku con express on node con https ,. Come faccio a identificare il protocollo per forzare un reindirizzamento a https con nodejs su heroku?

La mia app è solo un semplice server http, non si rende (ancora) conto che heroku gli sta inviando richieste 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
Il supporto di Heroku ha risposto alla mia domanda precedente e non l'ho già trovato pubblicato qui, quindi ho pensato di pubblicarlo in pubblico e condividere la conoscenza. Passano molte informazioni sulla richiesta originale con le sue intestazioni di richiesta precedute da una "x-". Ecco il codice che sto usando ora (in cima alle mie definizioni di percorso):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 quindi ho capito che controlli https come questo e reindirizza se necessario. Ma esiste un modo per reindirizzare a livello di DNS con il provider del nome di dominio. Quindi, prima che il browser risolva il DNS, è già su https. Perché con questo approccio è che, penso data la mia conoscenza dei reindirizzamenti, una volta che la richiesta viene effettuata su http e poi di nuovo su https. Quindi, se sono stati inviati dati sensibili, sono stati inviati tramite http una volta. poi su https. Il che sconfigge lo scopo. Per favore fatemi sapere se sbaglio.
Muhammad Umer

@ MuhammadUmer, il tuo ragionamento sembra puntare qui, hai mai scoperto di più?
Karoh

ho semplicemente usato cloudflare come server dei nomi che funziona come nginx e mi consente di reindirizzare alla versione ssl semplicemente facendo clic sul pulsante di attivazione / disattivazione. inoltre potresti fare questo: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/… Inoltre, di solito nessuno invia i dati subito, di solito atterrano sul modulo e poi inviano. quindi su codice lato server, server DNS, intestazione http, javascript puoi controllare e reindirizzare a https developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
Muhammad Umer

Risposte:


107

Ad oggi, 10 ottobre 2014 , utilizzando lo stack Heroku Cedar e ExpressJS ~ 3.4.4 , ecco un set di codice funzionante.

La cosa principale da ricordare qui è che STIAMO distribuendo su Heroku. La terminazione SSL avviene al bilanciamento del carico, prima che il traffico crittografato raggiunga l'app del nodo. È possibile verificare se https è stato utilizzato per effettuare la richiesta con req.headers ['x-forwarded-proto'] === 'https' .

Non dobbiamo preoccuparci di avere certificati SSL locali all'interno dell'app, ecc. Come potresti fare se ospitassi in altri ambienti. Tuttavia, dovresti prima ottenere un componente aggiuntivo SSL applicato tramite i componenti aggiuntivi Heroku se utilizzi il tuo certificato, sottodomini ecc.

Quindi aggiungi semplicemente quanto segue per eseguire il reindirizzamento da qualsiasi cosa diversa da HTTPS a HTTPS. Questo è molto vicino alla risposta accettata sopra, ma:

  1. Assicura di utilizzare "app.use" (per tutte le azioni, non solo per get)
  2. Esternalizza esplicitamente la logica forceSsl in una funzione dichiarata
  3. Non usa "*" con "app.use" - in realtà non è riuscito quando l'ho testato.
  4. Qui, voglio solo SSL in produzione. (Cambia in base alle tue esigenze)

Codice:

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

Nota per gli utenti di SailsJS (0.10.x). Puoi semplicemente creare una policy (enforceSsl.js) all'interno di api / policies:

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

Quindi fare riferimento da config / policies.js insieme a qualsiasi altra politica, ad esempio:

'*': ['authenticated', 'enforceSsl']


1
Una nota sull'utilizzo di una politica delle vele: come indicato in sailsjs.org/#/documentation/concepts/Policies : "Le mappature delle politiche predefinite non" cascano "o" gocciolano verso il basso ". Le mappature specificate per le azioni del controller sovrascriveranno la mappatura predefinita. " Ciò significa che non appena disponi di altri criteri per controller / azioni specifici, dovrai assicurarti di aggiungere "enforceSsl" su tali controller / azioni.
Manuel Darveau

2
"La tabella seguente elenca altre piccole ma importanti modifiche in Express 4: ... La funzione app.configure () è stata rimossa. Utilizza la funzione process.env.NODE_ENV o app.get ('env') per rilevare l'ambiente e configurare l'app di conseguenza. "
Kevin Wheeler

9
Inoltre, tieni res.redirectpresente che l'impostazione predefinita è un reindirizzamento 302 (almeno in Express 4.x). Per motivi SEO e di memorizzazione nella cache, probabilmente preferisci un reindirizzamento 301. Sostituisci la riga corrispondente conreturn res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
Kevin Wheeler

6
Nota: dentro Express 4.x, rimuovi la app.configurelinea e usa solo la pozione interna. app.configureè un codice legacy e non è più incluso in Express.
Augie Gardner

96

La risposta è usare l'intestazione di "x-forwarded-proto" che Heroku trasmette in quanto fa il suo proxy thingamabob. (nota a margine: passano anche molte altre variabili x che potrebbero essere utili, controllale ).

Il mio codice:

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

Grazie Brandon, stavo solo aspettando quella cosa di ritardo di 6 ore che non mi avrebbe permesso di rispondere alla mia domanda.


4
questo non permetterebbe ad altri metodi che GETattraverso?
Jed Schmidt

1
@ Aaron: Beh, potresti perdere informazioni se reindirizzassi in modo trasparente una richiesta POST. Penso che dovresti restituire 400 su richieste diverse da GET per http.
theodorton

3
Potresti && process.env.NODE_ENV === "production"inserire un nel tuo condizionale se vuoi che funzioni solo nel tuo ambiente di produzione.
keepitreal

307 (reindirizzamento con lo stesso metodo) è probabilmente migliore di un errore 400.
Beni Cherniavsky-Paskin

Ci sono più problemi con questa risposta, vedi la prossima risposta di seguito ( stackoverflow.com/a/23894573/14193 ) e valuta questa in basso.
Neil

22

La risposta accettata ha un dominio hardcoded, che non è troppo buono se hai lo stesso codice su più domini (es: dev-yourapp.com, test-yourapp.com, yourapp.com).

Usa questo invece:

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


Funziona bene. Ho duno perché ho dovuto sostituire req.hostnamecon req.headers.hostforse la versione express che sono in 4.2
Jeremy Piednoel


6

Se desideri testare l' x-forwarded-protointestazione sul tuo localhost, puoi utilizzare nginx per configurare un file vhost che invia in proxy tutte le richieste all'app del nodo. Il tuo file di configurazione vhost nginx potrebbe assomigliare a questo

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

I bit importanti qui sono che stai inviando tutte le richieste alla porta localhost 3000 (qui è dove è in esecuzione la tua app del nodo) e stai impostando un gruppo di intestazioni tra cui X-Forwarded-Proto

Quindi nella tua app rileva quell'intestazione come al solito

Esprimere

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

host

Infine devi aggiungere questa riga al tuo hostsfile

127.0.0.1 dummy.com

6

Dovresti dare un'occhiata a heroku-ssl-redirect . Esso funziona magicamente!

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

Se stai utilizzando cloudflare.com come CDN in combinazione con heroku, puoi abilitare il reindirizzamento SSL automatico all'interno di cloudflare facilmente in questo modo:

  1. Accedi e vai alla tua dashboard

  2. Seleziona Regole pagina

    Seleziona Regole pagina

  3. Aggiungi il tuo dominio, ad es. Www.example.com e attiva sempre l'uso di https Attiva sempre usa https

3

Gli utenti di loopback possono utilizzare una versione leggermente adattata di arcseldon answer come middleware:

server / middleware / 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());

2

Questo è un modo più specifico per Express per farlo.

app.enable('trust proxy');
app.use('*', (req, res, next) => {
  if (req.secure) {
    return next();
  }
  res.redirect(`https://${req.hostname}${req.url}`);
});

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

0

Con app.use e URL dinamico. Funziona sia localmente che su Heroku per me

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

-1

Il controllo del protocollo nell'intestazione X-Forwarded-Proto funziona bene su Heroku, proprio come ha sottolineato Derek. Per quello che vale, ecco una sintesi del middleware Express che utilizzo e del relativo test.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.