web-dev-qa-db-fra.com

Connexion / redirection HTTPS automatique avec node.js / express

J'ai essayé de configurer HTTPS avec un projet node.js sur lequel je travaille. J'ai essentiellement suivi le documentation de node.js pour cet exemple:

// curl -k https://localhost:8000/
var https = require('https');
var fs = require('fs');

var options = {
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world\n");
}).listen(8000);

Maintenant, quand je fais

curl -k https://localhost:8000/

Je reçois

hello world

comme prévu. Mais si je le fais

curl -k http://localhost:8000/

Je reçois

curl: (52) Empty reply from server

Rétrospectivement, cela semble évident que cela fonctionnerait de cette façon, mais en même temps, les personnes qui visitent mon projet ne vont pas taper https : // yadayada, et je veux que tout le trafic soit en https à partir du moment où ils ont visité le site.

Comment puis-je obtenir un nœud (et Express, car c'est la structure que j'utilise), pour transférer tout le trafic entrant à https, qu'il ait été spécifié ou non? Je n'ai pas été en mesure de trouver de documentation à ce sujet. Ou est-il simplement supposé que, dans un environnement de production, le noeud a quelque chose devant lui (par exemple, nginx) qui gère ce type de redirection?

Ceci est ma première incursion dans le développement Web, alors pardonnez-moi mon ignorance si cela est une évidence.

154
Jake

Ryan, merci de m'avoir orienté dans la bonne direction. J'ai étoffé votre réponse (2ème paragraphe) avec un peu de code et ça marche. Dans ce scénario, ces extraits de code sont placés dans mon application express:

// set up plain http server
var http = express.createServer();

// set up a route to redirect http to https
http.get('*', function(req, res) {  
    res.redirect('https://' + req.headers.Host + req.url);

    // Or, if you don't want to automatically detect the domain name from the request header, you can hard code it:
    // res.redirect('https://example.com' + req.url);
})

// have it listen on 8080
http.listen(8080);

Le serveur express https écoute ATM sur 3000. J'ai configuré ces règles iptables afin que le nœud n'ait pas à s'exécuter en tant que root:

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 3000

Tous ensemble, cela fonctionne exactement comme je le voulais.

157
Jake

Si vous suivez les ports conventionnels puisque HTTP essaie le port 80 par défaut et que HTTPS essaie le port 443 par défaut, vous pouvez simplement avoir deux serveurs sur le même ordinateur: Voici le code:

var https = require('https');

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

https.createServer(options, function (req, res) {
    res.end('secure!');
}).listen(443);

// Redirect from http port 80 to https
var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(301, { "Location": "https://" + req.headers['Host'] + req.url });
    res.end();
}).listen(80);

Testez avec https:

$ curl https://127.0.0.1 -k
secure!

Avec http:

$ curl http://127.0.0.1 -i
HTTP/1.1 301 Moved Permanently
Location: https://127.0.0.1/
Date: Sun, 01 Jun 2014 06:15:16 GMT
Connection: keep-alive
Transfer-Encoding: chunked

Plus de détails: Nodejs HTTP et HTTPS sur le même port

112
basarat

Grâce à ce type: https://www.tonyerwin.com/2014/09/redirecting-http-to-https-with-nodejs.html

app.use (function (req, res, next) {
        if (req.secure) {
                // request was via https, so do no special handling
                next();
        } else {
                // request was via http, so redirect to https
                res.redirect('https://' + req.headers.Host + req.url);
        }
});
67
Jeremias Binder

Avec Nginx, vous pouvez tirer parti de l’en-tête "x-forwarded-proto":

function ensureSec(req, res, next){
    if (req.headers["x-forwarded-proto"] === "https"){
       return next();
    }
    res.redirect("https://" + req.headers.Host + req.url);  
}
23
ccyrille

À partir de la version 0.4.12, nous n’avons aucune façon propre d’écouter HTTP et HTTPS sur le même port à l’aide des serveurs HTTP/HTTPS de Node.

Certaines personnes ont résolu ce problème en faisant en sorte que le serveur HTTPS de Node (qui fonctionne également avec Express.js) écoute 443 (ou un autre port) et ait également un petit serveur http lié à 80 et redirige les utilisateurs vers le port sécurisé.

Si vous devez absolument pouvoir gérer les deux protocoles sur un seul port, vous devez installer nginx, lighttpd, Apache ou un autre serveur Web sur ce port et agir en tant que proxy inverse pour Node.

12
Ryan Olds

Cette réponse doit être mise à jour pour fonctionner avec Express 4.0. Voici comment j'ai fait fonctionner le serveur http séparé:

var express = require('express');
var http = require('http');
var https = require('https');

// Primary https app
var app = express()
var port = process.env.PORT || 3000;
app.set('env', 'development');
app.set('port', port);
var router = express.Router();
app.use('/', router);
// ... other routes here
var certOpts = {
    key: '/path/to/key.pem',
    cert: '/path/to/cert.pem'
};
var server = https.createServer(certOpts, app);
server.listen(port, function(){
    console.log('Express server listening to port '+port);
});


// Secondary http app
var httpApp = express();
var httpRouter = express.Router();
httpApp.use('*', httpRouter);
httpRouter.get('*', function(req, res){
    var Host = req.get('Host');
    // replace the port in the Host
    Host = Host.replace(/:\d+$/, ":"+app.get('port'));
    // determine the redirect destination
    var destination = ['https://', Host, req.url].join('');
    return res.redirect(destination);
});
var httpServer = http.createServer(httpApp);
httpServer.listen(8080);
8
NoBrainer

Je trouve que le protocole req.fonctionne lorsque j'utilise Express (je n'ai pas testé sans, mais je soupçonne que cela fonctionne). en utilisant le noeud actuel 0.10.22 avec express 3.4.3

app.use(function(req,res,next) {
  if (!/https/.test(req.protocol)){
     res.redirect("https://" + req.headers.Host + req.url);
  } else {
     return next();
  } 
});
6
Catalyst

Si votre application est derrière un proxy approuvé (par exemple, un AWS ELB ou un nginx correctement configuré), ce code devrait fonctionner:

app.enable('trust proxy');
app.use(function(req, res, next) {
    if (req.secure){
        return next();
    }
    res.redirect("https://" + req.headers.Host + req.url);
});

Remarques:

  • Cela suppose que vous hébergez votre site sur 80 et 443, sinon vous devrez changer le port lorsque vous redirigez
  • Cela suppose également que vous mettez fin à SSL sur le proxy. Si vous utilisez SSL de bout en bout, utilisez la réponse de @basarat ci-dessus. SSL de bout en bout est la meilleure solution.
  • app.enable ('trust proxy') permet à express de vérifier l'en-tête X-Forwarded-Proto
6
Jamie McCrindle

La plupart des réponses suggèrent d'utiliser l'en-tête req.headers.Host.

L'en-tête Host est requis par HTTP 1.1, mais il est en fait facultatif, car l'en-tête pourrait ne pas être envoyé par un client HTTP et node/express acceptera cette demande.

Vous pouvez demander: quel client HTTP (par exemple: navigateur) peut envoyer une demande sans cet en-tête? Le protocole HTTP est très trivial. Vous pouvez créer une requête HTTP en quelques lignes de code pour ne pas envoyer d’en-tête d’hôte. Si une requête malformée est reçue, une exception est émise et, en fonction de la manière dont vous gérez ces exceptions, votre serveur sera arrêté.

Donc toujours valider toutes les entrées. Ce n'est pas de la paranoïa, j'ai reçu des demandes sans l'en-tête Host dans mon service.

En outre, ne jamais traiter les URL comme des chaînes. Utilisez le module url du noeud pour modifier des parties spécifiques d'une chaîne. Traiter les URL en tant que chaînes peut être exploité de nombreuses manières. Ne le fais pas.

4
arboreal84

Vous pouvez utiliser le module express-force-https :

npm install --save express-force-https

var express = require('express');
var secure = require('express-force-https');

var app = express();
app.use(secure);
4
Mateus Dal Bianco
var express = require('express');
var app = express();

app.get('*',function (req, res) {
    res.redirect('https://<domain>' + req.url);
});

app.listen(80);

C'est ce que nous utilisons et cela fonctionne très bien!

3
Nick Kotenberg

vous pouvez utiliser le module "net" pour écouter HTTP et HTTPS sur le même port

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

var net=require('net');
var handle=net.createServer().listen(8000)

var options = {
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world\n");
}).listen(handle);

http.createServer(function(req,res){
  res.writeHead(200);
  res.end("hello world\n");
}).listen(handle)
3
luofei614

J'utilise la solution proposée par Basarat mais je dois aussi survertir le port car j'avais 2 ports différents pour les protocoles HTTP et HTTPS.

res.writeHead(301, { "Location": "https://" + req.headers['Host'].replace(http_port,https_port) + req.url });

Je préfère également utiliser un port non standard afin de démarrer nodejs sans privilèges root. J'aime 8080 et 8443 parce que je viens de nombreuses années de programmation sur Tomcat.

Mon dossier complet devient

    var fs = require('fs');
var http = require('http');
var http_port    =   process.env.PORT || 8080; 
var app = require('express')();

// HTTPS definitions
var https = require('https');
var https_port    =   process.env.PORT_HTTPS || 8443; 
var options = {
   key  : fs.readFileSync('server.key'),
   cert : fs.readFileSync('server.crt')
};

app.get('/', function (req, res) {
   res.send('Hello World!');
});

https.createServer(options, app).listen(https_port, function () {
   console.log('Magic happens on port ' + https_port); 
});

// Redirect from http port to https
http.createServer(function (req, res) {
    res.writeHead(301, { "Location": "https://" + req.headers['Host'].replace(http_port,https_port) + req.url });
    console.log("http request, will go to >> ");
    console.log("https://" + req.headers['Host'].replace(http_port,https_port) + req.url );
    res.end();
}).listen(http_port);

Ensuite, j'utilise iptable pour forworder le trafic 80 et 443 sur mes ports HTTP et HTTPS.

Sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
Sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8443
3
Lorenzo Eccher

Cela a fonctionné pour moi:

app.use(function(req,res,next) {
    if(req.headers["x-forwarded-proto"] == "http") {
        res.redirect("https://[your url goes here]" + req.url, next);
    } else {
        return next();
    } 
});
2
Shummy1991

Vous pouvez instancier 2 serveurs Node.js - un pour HTTP et HTTPS

Vous pouvez également définir une fonction de configuration que les deux serveurs exécuteront, de sorte que vous n'ayez pas à écrire beaucoup de code dupliqué.

Voici comment je l'ai fait: (en utilisant restify.js, mais cela devrait fonctionner pour express.js, ou le noeud lui-même aussi)

http://qugstart.com/blog/node-js/node-js-restify-server-with-both-http-and-https/

1
awaage

Cela a fonctionné pour moi:

/* Headers */
require('./security/Headers/HeadersOptions').Headers(app);

/* Server */
const ssl = {
    key: fs.readFileSync('security/ssl/cert.key'),
    cert: fs.readFileSync('security/ssl/cert.pem')
};
//https server
https.createServer(ssl, app).listen(443, '192.168.1.2' && 443, '127.0.0.1');
//http server
app.listen(80, '192.168.1.2' && 80, '127.0.0.1');
app.use(function(req, res, next) {
    if(req.secure){
        next();
    }else{
        res.redirect('https://' + req.headers.Host + req.url);
    }
});

Recommander d'ajouter les en-têtes avant de rediriger vers https

Maintenant, quand tu fais:

curl http://127.0.0.1 --include

Vous obtenez:

HTTP/1.1 302 Found
//
Location: https://127.0.0.1/
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 40
Date: Thu, 04 Jul 2019 09:57:34 GMT
Connection: keep-alive

Found. Redirecting to https://127.0.0.1/

J'utilise express 4.17.1