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

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']


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.


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/


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


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

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

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

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

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

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

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

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.