Heroku NodeJS http vers https ssl redirection forcée

105

J'ai une application en cours d'exécution sur heroku avec express sur nœud avec https ,. Comment identifier le protocole pour forcer une redirection vers https avec nodejs sur heroku?

Mon application n'est qu'un simple serveur http, elle ne réalise pas (encore) qu'heroku lui envoie des requêtes 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
la source
6
L'assistance Heroku a répondu à ma question ci-dessus, et je ne l'ai pas déjà trouvée publiée ici, alors j'ai pensé la publier en public et partager mes connaissances. Ils transmettent beaucoup d'informations sur la demande d'origine avec ses en-têtes de demande précédés d'un «x-». Voici le code que j'utilise maintenant (en haut de mes définitions d'itinéraire):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 donc je comprends que vous vérifiez https comme celui-ci et que vous redirigez si nécessaire. Mais existe-t-il un moyen de rediriger au niveau DNS avec votre fournisseur de nom de domaine. Donc, avant que le navigateur ne résolve le DNS, il est déjà à https. Parce qu'avec cette approche, je pense que, compte tenu de ma connaissance des redirections, qu'une fois la demande est faite sur http, puis à nouveau sur https. Ainsi, si des données sensibles ont été envoyées, elles ont été envoyées une fois sur http. puis sur https. Ce qui va à l'encontre du but. S'il vous plaît laissez-moi savoir si je me trompe.
Muhammad Umer
@MuhammadUmer, votre raisonnement semble juste ici, en avez-vous déjà découvert plus?
Karoh
J'ai simplement utilisé cloudflare comme serveur de noms qui fonctionne comme nginx, et me permet de rediriger vers la version ssl simplement en cliquant sur le bouton bascule. vous pouvez également faire ceci: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/... De plus, généralement personne n'envoie de données tout de suite, ils atterrissent généralement sur le formulaire puis les soumettent. donc sur le code côté serveur, serveur dns, en-tête http, javascript, vous pouvez vérifier et rediriger vers https developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
Muhammad Umer

Réponses:

107

À compter d'aujourd'hui, 10 octobre 2014 , en utilisant Heroku Cedar stack et ExpressJS ~ 3.4.4 , voici un ensemble de code fonctionnel.

La principale chose à retenir ici est que nous déployons sur Heroku. La résiliation SSL se produit au niveau de l'équilibreur de charge, avant que le trafic chiffré n'atteigne votre application de nœud. Il est possible de tester si https a été utilisé pour faire la requête avec req.headers ['x-forwarded-proto'] === 'https' .

Nous n'avons pas besoin de nous soucier d'avoir des certificats SSL locaux dans l'application, etc., comme vous le feriez si vous hébergez dans d'autres environnements. Cependant, vous devez d'abord appliquer un module complémentaire SSL via les modules complémentaires Heroku si vous utilisez votre propre certificat, sous-domaines, etc.

Ensuite, ajoutez simplement ce qui suit pour effectuer la redirection de tout autre chose que HTTPS vers HTTPS. C'est très proche de la réponse acceptée ci-dessus, mais:

  1. S'assure que vous utilisez "app.use" (pour toutes les actions, pas seulement pour obtenir)
  2. Externalisation explicite de la logique forceSsl dans une fonction déclarée
  3. N'utilise pas '*' avec "app.use" - cela a échoué lorsque je l'ai testé.
  4. Ici, je ne veux que du SSL en production. (Changez selon vos besoins)

Code:

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

Remarque pour les utilisateurs de SailsJS (0.10.x). Vous pouvez simplement créer une stratégie (enforceSsl.js) dans 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();
  }
};

Ensuite, faites référence à config / policies.js avec toute autre politique, par exemple:

'*': ['authentifié', 'enforceSsl']

Arcseldon
la source
1
Une note sur l'utilisation d'une politique de voiles: Comme indiqué dans sailsjs.org/#/documentation/concepts/Policies : "Les mappages de politique par défaut ne" cascade "ou" ne se répercutent pas vers le bas. "Les mappages spécifiés pour les actions du contrôleur remplaceront le mappage par défaut. " Cela signifie que dès que vous avez d'autres politiques pour un contrôleur / une action spécifique, vous devrez vous assurer d'ajouter «enforceSsl» sur ces contrôleurs / actions.
Manuel Darveau
2
«Le tableau suivant répertorie d'autres modifications mineures mais importantes dans Express 4: ... La fonction app.configure () a été supprimée. Utilisez la fonction process.env.NODE_ENV ou app.get ('env') pour détecter l'environnement et configurer l'application en conséquence. "
Kevin Wheeler
9
Notez également que res.redirectcela par défaut est une redirection 302 (au moins dans express 4.x). Pour des raisons de référencement et de mise en cache, vous souhaitez probablement une redirection 301 à la place. Remplacez la ligne correspondante parreturn res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
Kevin Wheeler
6
Remarque: Dans Express 4.x, retirez la app.configureligne et utilisez simplement la potion intérieure. app.configureest un code hérité et n'est plus inclus dans express.
Augie Gardner
96

La réponse est d'utiliser l'en-tête de 'x-forwarded-proto' que Heroku transmet comme il le fait pour le proxy thingamabob. (note latérale: ils transmettent également plusieurs autres variables x qui peuvent être utiles, vérifiez-les ).

Mon code:

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

Merci Brandon, j'attendais juste ce retard de 6 heures qui ne me permettrait pas de répondre à ma propre question.

Derek Bredensteiner
la source
4
cela ne laisserait-il pas passer d'autres méthodes GET?
Jed Schmidt
1
@Aaron: Eh bien, vous perdriez potentiellement des informations si vous redirigiez de manière transparente une requête POST. Je pense que vous devriez renvoyer un 400 sur d'autres demandes que GET pour http.
theodorton
3
Vous pouvez ajouter un && process.env.NODE_ENV === "production"à votre conditionnel si vous souhaitez qu'il fonctionne uniquement dans votre environnement de production.
keepitreal
307 (redirection avec la même méthode) est probablement mieux qu'une erreur 400.
Beni Cherniavsky-Paskin
Il y a plusieurs problèmes avec cette réponse, voir la réponse suivante ci-dessous ( stackoverflow.com/a/23894573/14193 ) et notez celle-ci à la baisse.
Neil
22

La réponse acceptée contient un domaine codé en dur, ce qui n'est pas très bon si vous avez le même code sur plusieurs domaines (par exemple: dev-yourapp.com, test-yourapp.com, yourapp.com).

Utilisez plutôt ceci:

/* 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
la source
Fonctionne bien. Je ne sais pas pourquoi je devais juste remplacer req.hostnamepar la req.headers.hostversion peut-être express que je suis en 4.2
Jeremy Piednoel
16

J'ai écrit un petit module de nœud qui applique SSL sur les projets express. Il fonctionne à la fois dans des situations standard et en cas de reverse proxies (Heroku, nodejitsu, etc.)

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

Florian
la source
6

Si vous souhaitez tester l'en- x-forwarded-prototête sur votre hôte local, vous pouvez utiliser nginx pour configurer un fichier vhost qui envoie par proxy toutes les demandes à votre application de nœud. Votre fichier de configuration nginx vhost pourrait ressembler à ceci

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

Les éléments importants ici sont que vous transmettez par proxy toutes les demandes au port localhost 3000 (c'est là que votre application de nœud s'exécute) et que vous configurez un tas d'en-têtes, y compris X-Forwarded-Proto

Ensuite, dans votre application, détectez cet en-tête comme d'habitude

Express

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

Hôtes

Enfin, vous devez ajouter cette ligne à votre hostsfichier

127.0.0.1 dummy.com
simo
la source
6

Vous devriez jeter un oeil à heroku-ssl-redirect . Il fonctionne comme un charme!

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
la source
4

Si vous utilisez cloudflare.com comme CDN en combinaison avec heroku, vous pouvez activer la redirection SSL automatique dans cloudflare facilement comme ceci:

  1. Connectez-vous et accédez à votre tableau de bord

  2. Sélectionnez les règles de la page

    Sélectionnez les règles de la page

  3. Ajoutez votre domaine, par exemple www.example.com et activez toujours utiliser https Activer toujours utiliser https
electronix384128
la source
3

Les utilisateurs de bouclage peuvent utiliser une version légèrement adaptée de arcseldon answer comme middleware:

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

serveur / serveur.js

var forceSSL = require('./middleware/forcessl.js');
app.use(forceSSL());
Bunker
la source
2

C'est une manière plus spécifique de faire ceci.

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

Avec app.use et URL dynamique. Fonctionne à la fois localement et sur Heroku pour moi

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

Vérifier le protocole dans l'en-tête X-Forwarded-Proto fonctionne bien sur Heroku, tout comme Derek l'a souligné. Pour ce que ça vaut, voici un aperçu du middleware Express que j'utilise et de son test correspondant.

Peter Marklund
la source