Connessione / reindirizzamento HTTPS automatico con node.js / express

Ho cercato di ottenere la configurazione HTTPS con un progetto node.js su cui sto lavorando. Ho essenzialmente seguito la documentazione node.js per questo esempio:

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

Ora, quando lo faccio

 curl -k https://localhost:8000/ 

ottengo

 hello world 

come previsto. Ma se lo faccio

 curl -k http://localhost:8000/ 

ottengo

 curl: (52) Empty reply from server 

In retrospettiva, sembra ovvio che funzionerebbe in questo modo, ma allo stesso tempo, le persone che alla fine visitano il mio progetto non digiteranno https : // yadayada, e voglio che tutto il traffico sia https dal momento in cui colpiscono il sito.

Come posso ottenere il nodo (ed Express in quanto tale è il framework che sto usando) per consegnare tutto il traffico in arrivo a https, indipendentemente dal fatto che sia stato specificato o meno? Non sono stato in grado di trovare alcuna documentazione che abbia affrontato questo problema. O è solo scontato che in un ambiente di produzione, il nodo abbia qualcosa che lo precede (es. Nginx) che gestisce questo tipo di reindirizzamento?

Questa è la mia prima incursione nello sviluppo del web, quindi per favore perdona la mia ignoranza se questo è qualcosa di ovvio.

Ryan, grazie per avermi indicato nella giusta direzione. Ho arricchito la tua risposta (2 ° paragrafo) un po ‘con un po’ di codice e funziona. In questo scenario, questi snippet di codice vengono inseriti nella mia app 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); 

Il server https express ascolta ATM su 3000. Ho impostato queste regole di iptables in modo che il nodo non debba essere eseguito come 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 

Tutti insieme, questo funziona esattamente come volevo.

Se segui le porte convenzionali poiché HTTP tenta la porta 80 per impostazione predefinita e HTTPS prova la porta 443 per impostazione predefinita puoi semplicemente avere due server sulla stessa macchina: Ecco il codice:

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

Prova con https:

 $ curl https://127.0.0.1 -k secure! 

Con 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 

Ulteriori dettagli: Nodejs HTTP e HTTPS sulla stessa porta

Con Nginx puoi sfruttare l’intestazione “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); } 

A partire dalla 0.4.12 non abbiamo un vero modo pulito per ascoltare HTTP e HTTPS sulla stessa porta usando i server HTTP / HTTPS del nodo.

Alcune persone hanno risolto questo problema avendo il server HTTPS di Node (funziona anche con Express.js) ascoltando 443 (o qualche altra porta) e avendo anche un piccolo server http associato a 80 e reindirizzando gli utenti alla porta sicura.

Se è assolutamente necessario essere in grado di gestire entrambi i protocolli su una singola porta, è necessario inserire nginx, lighttpd, apache o qualche altro server Web su quella porta e agire come proxy inverso per il nodo.

Grazie a questo ragazzo: 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); } }); 

Questa risposta deve essere aggiornata per funzionare con Express 4.0. Ecco come ho ottenuto il server http separato per funzionare:

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

Trovo req.protocol funziona quando sto usando express (non ho testato senza ma ho il sospetto che funzioni). utilizzando il nodo corrente 0.10.22 con 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(); } }); 

La maggior parte delle risposte suggeriscono di usare l’intestazione req.headers.host.

L’intestazione Host è richiesta da HTTP 1.1, ma in realtà è facoltativa poiché l’intestazione potrebbe non essere effettivamente inviata da un client HTTP e node / express accetterà questa richiesta.

Potresti chiedere: quale client HTTP (ad esempio: browser) può inviare una richiesta mancante a quell’intestazione? Il protocollo HTTP è molto banale. È ansible creare una richiesta HTTP in poche righe di codice, non inviare un’intestazione host e se ogni volta che si riceve una richiesta non valida si genera un’eccezione e, a seconda di come vengono gestite tali eccezioni, questo può ridurre il server.

Quindi convalidare sempre tutti gli input . Questa non è paranoia, ho ricevuto richieste senza l’intestazione host nel mio servizio.

Inoltre, non trattare mai gli URL come stringhe . Utilizzare il modulo url del nodo per modificare parti specifiche di una stringa. Trattare gli URL come stringhe può essere sfruttato in molti molti modi. Non farlo

puoi usare il modulo “net” per ascoltare HTTP e HTTPS sulla stessa porta

 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) 
 var express = require('express'); var app = express(); app.get('*',function (req, res) { res.redirect('https://' + req.url); }); app.listen(80); 

Questo è ciò che usiamo e funziona alla grande!

Questo ha funzionato per me:

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

Puoi istanziare 2 server Node.js, uno per HTTP e HTTPS

È anche ansible definire una funzione di configurazione che entrambi i server eseguiranno, in modo da non dover scrivere molto codice duplicato.

Ecco come l’ho fatto: (usando restify.js, ma dovrebbe funzionare anche per express.js, o anche per il nodo stesso)

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

Se la tua app è dietro un proxy fidato (ad esempio un ELB AWS o un nginx configurato correttamente), questo codice dovrebbe funzionare:

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

Gli appunti:

  • Ciò presuppone che stai ospitando il tuo sito su 80 e 443, in caso contrario, dovrai cambiare la porta quando esegui il reindirizzamento
  • Ciò presuppone anche che tu stia chiudendo l’SSL sul proxy. Se stai facendo SSL end to end usa la risposta di @basarat sopra. L’end-to-end SSL è la soluzione migliore.
  • app.enable (‘trust proxy’) consente a express di controllare l’intestazione X-Forwarded-Proto

Io uso la soluzione proposta da Basarat, ma ho anche bisogno di sovrascrivere la porta perché avevo due porte diverse per i protocolli HTTP e HTTPS.

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

Preferisco anche usare non la porta standard così da avviare nodejs senza privilegi di root. Mi piacciono gli 8080 e gli 8443 perché vengo da molti anni di programmazione su Tomcat.

Il mio file completo diventa

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

Quindi utilizzo iptable per il forwording del traffico 80 e 443 sulle mie porte HTTP e 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 

Puoi usare il modulo 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);