229 votes

Connexion/redirection HTTPS automatique avec node.js/express

J'ai essayé de mettre en place le HTTPS dans un projet node.js sur lequel je travaille. J'ai essentiellement suivi le documentation 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 fais

curl -k http://localhost:8000/

Je reçois

curl: (52) Empty reply from server

Rétrospectivement, il semble évident que cela fonctionne de cette manière, 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 https dès qu'il arrive sur le site.

Comment puis-je faire en sorte que node (et Express, puisque c'est le framework que j'utilise) transfère tout le trafic entrant vers https, qu'il ait été spécifié ou non ? Je n'ai pas été en mesure de trouver une documentation qui a abordé cette question. Ou est-ce qu'il est simplement supposé que dans un environnement de production, node a quelque chose qui se trouve devant lui (par exemple nginx) qui gère ce type de redirection ?

Il s'agit de ma première incursion dans le domaine du développement web, alors pardonnez mon ignorance si c'est quelque chose d'évident.

0 votes

J'ai répondu à cette question de manière succincte ici : stackoverflow.com/a/23894573/1882064

6 votes

Pour ceux qui DEPLOIENT VERS HEROKU, les réponses à cette question ne vous aideront pas (vous obtiendrez "too many redirects") mais cette réponse d'une autre question

0 votes

Cela répond-il à votre question ? Heroku NodeJS http vers https ssl redirection forcée

222voto

Jake Points 625

Ryan, merci de me mettre dans la bonne direction. J'ai un peu étoffé ta réponse (2e paragraphe) avec du code et ça marche. Dans ce scénario, ces bouts de code sont placés dans mon application express :

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

// 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 https express écoute l'ATM sur 3000. J'ai mis en place ces règles iptables pour que le noeud 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

Dans l'ensemble, cela fonctionne exactement comme je le voulais.

Pour empêcher le vol de cookies via HTTP, voir cette réponse (dans les commentaires) ou utilisez ce code :

const session = require('cookie-session');
app.use(
  session({
    secret: "some secret",
    httpOnly: true,  // Don't let browser javascript access cookies.
    secure: true, // Only use cookies over https.
  })
);

19 votes

Question très importante (concernant la sécurité). Avant que la redirection ne se produise réellement, est-il possible pour un attaquant de renifler et de voler un cookie (ID de session) ?

5 votes

Comment puis-je corriger ce symptôme ? Error 310 (net::ERR_TOO_MANY_REDIRECTS): There were too many redirects

18 votes

En fait, cela semble mieux ... il suffit d'entourer la redirection de if(!req.secure){}

137voto

basarat Points 22425

Si vous suivez les ports conventionnels, puisque HTTP essaie le port 80 par défaut et HTTPS essaie le port 443 par défaut, vous pouvez simplement avoir deux serveurs sur la même machine : 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);

Test 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

7 votes

res.writeHead(301, etc.) ne fonctionnera correctement que pour les appels GET, car 301 ne dit pas au client d'utiliser la même méthode. Si vous voulez conserver la méthode utilisée (et tous les autres paramètres), vous devez utiliser la fonction res.writeHead(307, etc.) . Et si cela ne fonctionne toujours pas, vous devrez peut-être faire appel à des mandataires. Source : http://stackoverflow.com/a/17612942/1876359

28voto

ccyrille Points 404

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

6 votes

J'ai trouvé que req.headers["x-forwarded-proto"] === "https") n'était pas fiable, mais req.secure fonctionne !

0 votes

J'ai fait le même constat, cela semble très peu fiable.

2 votes

Req.secure est la bonne méthode, mais cela pose un problème si vous êtes derrière un proxy car req.secure est équivalent à proto == "https", mais derrière un proxy, express peut dire que votre proto est https,http

13voto

Ryan Olds Points 3022

À partir de la version 0.4.12, nous n'avons pas vraiment de moyen propre d'écouter HTTP et HTTPS sur le même port en utilisant les serveurs HTTP/HTTPS de Node.

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

Si vous devez absolument être capable de gérer les deux protocoles sur un seul port, alors vous devez mettre nginx, lighttpd, apache, ou un autre serveur web sur ce port et le faire agir comme un proxy inverse pour Node.

0 votes

Merci Ryan. Par "...avoir un petit serveur http qui se lie à 80 et redirige...". Je suppose que vous voulez dire un autre nœud serveur http. Je pense avoir une idée de la manière dont cela pourrait fonctionner, mais pouvez-vous m'indiquer un exemple de code ? En outre, savez-vous si une solution plus "propre" à ce problème est quelque part dans la feuille de route de node ?

0 votes

Votre application Node.js peut avoir plusieurs serveurs http(s). J'ai vérifié les problèmes de Node.js ( github.com/joyent/node/issues ) et la liste de diffusion ( groups.google.com/group/nodejs ) et n'a pas vu de problèmes signalés, mais a vu quelques messages sur le problème sur la liste de diffusion. Pour autant que je sache, ce n'est pas dans les tuyaux. Je vous recommande de le signaler sur Github et de voir les réactions que vous obtiendrez.

0 votes

Question très importante (concernant la sécurité). Avant que la redirection ne se produise réellement, est-il possible pour un "attaquant" de renifler et de voler un cookie (ID de session) ?

6voto

luofei614 Points 55

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)

5 votes

Lorsque je l'exécute sur Node 0.8, seul le dernier serveur qui appelle .listen semble répondre. Dans ce cas, HTTP fonctionne, mais pas HTTPS. Si j'inverse l'ordre de .createServer, alors HTTP fonctionne mais pas HTTPS :(

1 votes

Cela ne fonctionne pas comme décrit. Je peux confirmer le problème que Joe a vu.

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