web-dev-qa-db-fra.com

Comment forcer SSL / https dans Express.js

J'essaie de créer un middleware pour Express.js pour rediriger tout le trafic non sécurisé (port 80) vers le port SSL sécurisé (443). Malheureusement, aucune demande dans Express.js ne vous permet de déterminer si la demande provient de http ou https.

Une solution serait de rediriger chaque requête mais ce n'est pas une option pour moi.

Remarques:

  1. Il n'y a aucune possibilité de le gérer avec Apache ou autre chose. Il a à faire dans le nœud.

  2. Seul n serveur peut être activé dans l'application.

Comment résoudriez-vous cela?

55
Elias

Juste au cas où vous hébergez sur Heroku et que vous souhaitez simplement rediriger vers HTTPS quel que soit le port, voici la solution middleware que nous utilisons.

Cela ne dérange pas de rediriger si vous développez localement.

function requireHTTPS(req, res, next) {
  // The 'x-forwarded-proto' check is for Heroku
  if (!req.secure && req.get('x-forwarded-proto') !== 'https' && process.env.NODE_ENV !== "development") {
    return res.redirect('https://' + req.get('Host') + req.url);
  }
  next();
}

Vous pouvez l'utiliser avec Express (2.x et 4.x) comme ceci:

app.use(requireHTTPS);
48
Lavamantis

Bien que la question semble vieille d'un an, je voudrais y répondre car elle pourrait aider les autres. C'est vraiment très simple avec la dernière version d'expressjs (2.x). Créez d'abord la clé et le certificat à l'aide de ce code

openssl genrsa -out ssl-key.pem 1024

$ openssl req -new -key ssl-key.pem -out certrequest.csr .. tas d'invites

$ openssl x509 -req -in certrequest.csr -signkey ssl-key.pem -out ssl-cert.pem

Stockez les fichiers de certificats et de clés dans le dossier contenant app.js. Modifiez ensuite le fichier app.js et écrivez le code suivant avant express.createServer ()

var https = require('https');
var fs = require('fs');

var sslkey = fs.readFileSync('ssl-key.pem');
var sslcert = fs.readFileSync('ssl-cert.pem')

var options = {
    key: sslkey,
    cert: sslcert
};

Passez maintenant l'objet options dans la fonction createServer ()

express.createServer(options);

Terminé!

26
MeetM

Tout d'abord, laissez-moi voir si je peux clarifier le problème. Vous êtes limité à un (1) processus node.js, mais ce processus peut écouter sur deux (2) ports réseau, 80 et 443, non? (Lorsque vous dites un serveur, il n'est pas clair si vous voulez dire un processus ou un seul port réseau).

Compte tenu de cette contrainte, votre problème semble être, pour des raisons que vous ne fournissez pas, que vos clients se connectent en quelque sorte au mauvais port. C'est un cas bizarre pour Edge car par défaut, les clients feront des requêtes HTTP sur le port 80 et HTTPS sur le port 443. Et quand je dis "par défaut", je veux dire si aucun port spécifique n'est inclus dans les URL. Donc, à moins que vous n'utilisiez explicitement des URL entrecroisées comme http://example.com:44 et https://example.com:8 , vous ne devriez vraiment pas avoir du trafic entrecroisé sur votre site. Mais puisque vous avez posé la question, je suppose que vous devez l'avoir, bien que je parie que vous utilisez des ports non standard par opposition aux valeurs par défaut 80/443.

Donc, pour le fond: OUI, certains serveurs Web gèrent cela assez bien. Par exemple, si vous faites http://example.com:44 à nginx, il répondra avec une réponse HTTP 400 "Bad Request" indiquant "La requête HTTP ordinaire a été envoyée au port HTTPS". OUI, vous pouvez écouter à la fois 80 et 443 à partir du même processus node.js. Vous avez juste besoin de créer 2 instances distinctes de express.createServer(), donc ce n'est pas un problème. Voici un programme simple pour démontrer la manipulation des deux protocoles.

var fs = require("fs");
var express = require("express");

var http = express.createServer();

var httpsOptions = {
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem')
};

var https = express.createServer(httpsOptions);

http.all('*', function(req, res) {
  console.log("HTTP: " + req.url);
  return res.redirect("https://" + req.headers["Host"] + req.url);
});

http.error(function(error, req, res, next) {
  return console.log("HTTP error " + error + ", " + req.url);
});

https.error(function(error, req, res, next) {
  return console.log("HTTPS error " + error + ", " + req.url);
});

https.all('*', function(req, res) {
  console.log("HTTPS: " + req.url);
  return res.send("Hello, World!");
});

http.listen(80);

Et je peux tester cela via cURL comme ceci:

$ curl --include --silent http://localhost/foo
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Content-Type: text/html
Location: https://localhost/foo
Connection: keep-alive
Transfer-Encoding: chunked

<p>Moved Temporarily. Redirecting to <a href="https://localhost/foo">https://localhost/foo</a></p>

$ curl --include --silent --insecure https://localhost:443/foo
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 13
Connection: keep-alive

Hello, World!%

Et montrant la redirection de HTTP vers HTTPS ...

curl --include --silent --location --insecure 'http://localhost/foo?bar=bux'
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Content-Type: text/html
Location: https://localhost/foo?bar=bux
Connection: keep-alive
Transfer-Encoding: chunked

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 13
Connection: keep-alive

Hello, World!%

Cela fonctionnera donc pour servir les deux protocoles pour le cas normal et rediriger correctement. Cependant, les croisements ne fonctionnent pas du tout. Je crois qu'une requête entrecroisée frappant un serveur express ne sera pas acheminée via la pile du middleware, car elle sera traitée comme une erreur dès le début et ne sera même pas analysée correctement l'URI de la demande, ce qui est nécessaire pour l'envoyer via la chaîne de routage middleware. La pile express ne les reçoit même pas, je pense, car ce ne sont pas des demandes valides, donc elles sont ignorées quelque part dans le nœud TCP. Il est probablement possible d'écrire un serveur pour ce faire, et il existe peut-être déjà un module, mais vous devez l'écrire directement dans la couche TCP. Et vous devez détecter une requête HTTP régulière dans le premier bloc de données client qui atteint le port TCP et connectez cette connexion à un serveur HTTP au lieu de votre négociation TLS normale.

Lorsque j'effectue l'une de ces opérations, mes gestionnaires d'erreur express ne sont PAS appelés.

curl  --insecure https://localhost:80/foo
curl: (35) Unknown SSL protocol error in connection to localhost:80

curl http://localhost:443/foo
curl: (52) Empty reply from server
21
Peter Lyons

Basé sur la réponse d'Elias mais avec un code en ligne. Cela fonctionne si vous avez un nœud derrière nginx ou un équilibreur de charge. Nginx ou l'équilibreur de charge atteindra toujours le nœud avec un ancien http simple, mais il définit un en-tête afin que vous puissiez le distinguer.

app.use(function(req, res, next) {
  var schema = req.headers['x-forwarded-proto'];

  if (schema === 'https') {
    // Already https; don't do anything special.
    next();
  }
  else {
    // Redirect to https.
    res.redirect('https://' + req.headers.Host + req.url);
  }
});
10
Jonathan Tran

http.createServer (app.handle) .listen (80)

https.createServer (options, app.handle) .listen (443)

pour express 2x

3
TJ Holowaychuk

Ce code semble faire ce dont vous avez besoin: https://Gist.github.com/903596

2
robocat

Essayez cet exemple:

var express = require('express');
        var app = express();
        // set up a route to redirect http to https
        app.use(function (req, res, next) {
        if (!/https/.test(req.protocol)) {
            res.redirect("https://" + req.headers.Host + req.url);
        } else {
            return next();
        }
        });
        var webServer = app.listen(port, function () {
            console.log('Listening on port %d', webServer.address().port);
        });
2
Ashok Kumawat