109 votes

Heroku NodeJS http vers https ssl redirection forcée

J'ai une application en cours d'exécution sur Heroku avec Express.js sur Node.js avec https . Comment puis-je identifier le protocole pour forcer une redirection vers https avec Node.js sur Heroku ?

Mon application est juste un simple http -le serveur, il ne réalise pas (encore) que Heroku l'envoie. https -Demandes :

// Heroku provides the port they want you on in this environment variable (hint: it's not 80)
app.listen(process.env.PORT || 3000);

8 votes

Le support Heroku a répondu à ma question ci-dessus, et je n'ai pas trouvé de réponse déjà postée ici, alors j'ai pensé la poster en public et partager les connaissances. Ils transmettent beaucoup d'informations sur la requête originale avec ses en-têtes de requête préfixés par un 'x-'. Voici le code que j'utilise actuellement (en haut de mes définitions de route) : app.get('*',function(req,res,next){ if(req.headers['x-forwarded-proto']!='https') res.redirect('https://mypreferreddomain.com'+req.url) else next() })

1 votes

Ok j'ai compris que vous vérifiez pour https comme ça et redirigez si nécessaire. Mais y a-t-il un moyen de faire une redirection au niveau du DNS avec votre fournisseur de nom de domaine ? Ainsi, avant que le navigateur ne résolve le DNS, il est déjà en https. Parce qu'avec cette approche, je pense, étant donné ma connaissance des redirections, qu'une demande est faite sur http puis à nouveau sur https. Donc si des données sensibles ont été envoyées, elles ont été envoyées sur http une fois, puis sur https. Ce qui va à l'encontre du but recherché. S'il vous plaît laissez-moi savoir si je me trompe.

0 votes

@MuhammadUmer, votre raisonnement semble pointé du doigt ici, en avez-vous découvert davantage ?

112voto

arcseldon Points 400

A partir d'aujourd'hui, 10 octobre 2014 en utilisant Pile Cedar de Heroku y ExpressJS ~3.4.4 Voici un jeu de code fonctionnel.

La principale chose à retenir ici est que nous sommes en train de déployer sur Heroku. La terminaison SSL se produit au niveau de l'équilibreur de charge, avant que le trafic crypté n'atteigne votre application node. 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 pourriez le faire si vous hébergez dans d'autres environnements. Cependant, vous devriez d'abord faire appliquer un module complémentaire SSL via les modules complémentaires de Heroku si vous utilisez votre propre certificat, des sous-domaines, etc.

Il suffit ensuite d'ajouter ce qui suit pour effectuer la redirection de tout ce qui n'est pas HTTPS vers HTTPS. Ceci est très proche de la réponse acceptée ci-dessus, mais :

  1. Assurez-vous que vous utilisez "app.use" (pour toutes les actions, pas seulement get)
  2. Externalise explicitement la logique de forceSsl dans une fonction déclarée.
  3. N'utilise pas '*' avec "app.use" - cela a échoué lorsque j'ai testé.
  4. Ici, je ne veux SSL qu'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...
 });

Note pour les utilisateurs de SailsJS (0.10.x). Vous pouvez simplement créer une politique (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();
  }
};

Puis référence à partir de config/policies.js avec toute autre politique, par ex :

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

1 votes

Une remarque sur l'utilisation d'une politique de voiles : Comme indiqué dans sailsjs.org/#/documentation/concepts/Politiques : "Les mappages de stratégie par défaut ne sont pas "en cascade" ou "au compte-gouttes". Les mappages spécifiés pour les actions du contrôleur remplacent le mappage par défaut." Cela signifie que dès que vous avez d'autres politiques pour un contrôleur/action spécifique, vous devrez vous assurer d'ajouter 'enforceSsl' sur ces contrôleurs/actions.

2 votes

"Le tableau suivant énumère d'autres changements mineurs mais importants 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'app en conséquence. "

10 votes

Notez également que res.redirect cette redirection est par défaut une redirection 302 (au moins dans express 4.x). Pour des raisons de référencement et de mise en cache, vous voudrez probablement une redirection 301 à la place. Remplacez la ligne correspondante par return res.redirect(301, ['https://', req.get('Host'), req.url].join(''));

102voto

Derek Bredensteiner Points 1124

La réponse est d'utiliser l'en-tête 'x-forwarded-proto' que Heroku transmet en même temps que son proxy (remarque : ils transmettent aussi 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 le délai de 6 heures qui ne me laissait pas répondre à ma propre question.

4 votes

Cela ne permettrait-il pas à d'autres méthodes que GET par ?

1 votes

@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 les requêtes autres que GET pour http.

4 votes

Vous pourriez lancer un && process.env.NODE_ENV === "production" dans votre conditionnel si vous voulez qu'il ne fonctionne que dans votre environnement de production.

22voto

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 ceci à la place :

/* Redirect http to https */
app.get("*", function (req, res, next) {

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

});

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

0 votes

Il fonctionne bien. Je ne sais pas pourquoi je devais juste le remplacer. req.hostname con req.headers.host peut-être la version express que je suis en 4.2

17voto

florian Points 36

J'ai écrit un petit module node qui impose le SSL sur les projets express. Il fonctionne à la fois dans des situations standards et en cas de reverse proxies (Heroku, nodejitsu, etc.).

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

6voto

simo Points 1147

Si vous voulez tester le x-forwarded-proto sur votre hôte local, vous pouvez utiliser l'en-tête nginx pour configurer un fichier vhost qui transmet toutes les demandes à votre application node. Votre fichier de configuration vhost de nginx 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 envoyez toutes les requêtes vers le port 3000 de l'hôte local (c'est là que votre application node est exécutée) et que vous configurez un certain nombre d'en-têtes, notamment X-Forwarded-Proto

Puis, 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 hosts fichier

127.0.0.1 dummy.com

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X