Heroku NodeJS http zu https ssl erzwungene Weiterleitung

105

Ich habe eine Anwendung auf Heroku mit Express auf Knoten mit https. Wie identifiziere ich das Protokoll, um eine Umleitung zu https mit nodejs auf heroku zu erzwingen?

Meine App ist nur ein einfacher http-Server. Sie erkennt (noch) nicht, dass Heroku https-Anfragen sendet:

/* Heroku provides the port they want you on in this environment variable (hint: it's not 80) */
app.listen(process.env.PORT || 3000);
Derek Bredensteiner
quelle
6
Der Heroku-Support beantwortete meine obige Frage und ich fand sie hier noch nicht veröffentlicht. Daher dachte ich, ich würde sie öffentlich veröffentlichen und das Wissen teilen. Sie übergeben viele Informationen über die ursprüngliche Anfrage mit den Anforderungsheadern, denen ein 'x-' vorangestellt ist. Hier ist der Code, den ich jetzt verwende (oben in meinen Routendefinitionen):app.get('*',function(req,res,next){ if(req.headers['x-forwarded-proto']!='https') res.redirect('https://mypreferreddomain.com'+req.url) else next() })
Derek Bredensteiner
1
OK, ich verstehe, dass Sie nach https wie diesem suchen und bei Bedarf umleiten. Gibt es jedoch eine Möglichkeit, eine Umleitung auf DNS-Ebene mit Ihrem Domain-Namen-Anbieter durchzuführen? Bevor der Browser DNS auflöst, befindet er sich bereits unter https. Denn mit diesem Ansatz denke ich angesichts meines Wissens über Weiterleitungen, dass eine Anfrage einmal über http und dann wieder über https gestellt wird. Wenn also vertrauliche Daten gesendet wurden, wurden sie einmal über http gesendet. dann über https. Was den Zweck irgendwie zunichte macht. Bitte lassen Sie mich wissen, wenn ich falsch liege.
Muhammad Umer
@ MuhammadUmer, deine Argumentation scheint hier auf den Punkt zu kommen, hast du jemals mehr entdeckt?
Karoh
Ich habe einfach Cloudflare als Nameserver verwendet, der als Nginx funktioniert, und kann durch einfaches Klicken auf die Umschalttaste zur SSL-Version umleiten. Sie können dies auch tun: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/… Außerdem sendet normalerweise niemand sofort Daten, die normalerweise auf dem Formular landen und dann gesendet werden. So können Sie auf serverseitigem Code, DNS-Server, http-Header, Javascript überprüfen und zu https developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
Muhammad Umer

Antworten:

107

Ab heute, dem 10. Oktober 2014 , mit Heroku Cedar Stack und ExpressJS ~ 3.4.4 , ist hier ein funktionierender Satz von Code.

Die Hauptsache, an die wir uns erinnern sollten, ist, dass wir Heroku einsetzen. Die SSL-Beendigung erfolgt am Load Balancer, bevor der verschlüsselte Datenverkehr Ihre Knoten-App erreicht. Es ist möglich zu testen, ob https verwendet wurde, um die Anforderung mit req.headers ['x-forwarded-proto'] === 'https' zu stellen .

Wir müssen uns nicht darum kümmern, lokale SSL-Zertifikate in der App usw. zu haben, wie Sie es vielleicht tun, wenn Sie in anderen Umgebungen hosten. Sie sollten jedoch zuerst ein SSL-Add-On über Heroku-Add-Ons anwenden lassen, wenn Sie Ihr eigenes Zertifikat, Ihre eigenen Subdomains usw. verwenden.

Fügen Sie dann einfach Folgendes hinzu, um die Umleitung von etwas anderem als HTTPS zu HTTPS durchzuführen. Dies kommt der oben akzeptierten Antwort sehr nahe, aber:

  1. Stellt sicher, dass Sie "app.use" verwenden (für alle Aktionen, nicht nur get)
  2. Externalisiert die forceSsl-Logik explizit in eine deklarierte Funktion
  3. Verwendet '*' nicht mit "app.use" - dies ist tatsächlich fehlgeschlagen, als ich es getestet habe.
  4. Hier möchte ich nur SSL in der Produktion. (Ändern Sie nach Ihren Wünschen)

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...
}

Hinweis für Benutzer von SailsJS (0.10.x). Sie können einfach eine Richtlinie (erzwingenSsl.js) in API / Richtlinien erstellen:

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

Verweisen Sie dann zusammen mit anderen Richtlinien auf config / Policies.js, z.

'*': ['authentifiziert', 'erzwingenSsl']

arcseldon
quelle
1
Hinweis zur Verwendung einer Segelrichtlinie : Wie in sailsjs.org/#/documentation/concepts/Policies angegeben : "Standardrichtlinienzuordnungen" kaskadieren "oder" rieseln "nicht. Angegebene Zuordnungen für die Aktionen des Controllers überschreiben die Standardzuordnung. "" Dies bedeutet, dass Sie, sobald Sie andere Richtlinien für einen bestimmten Controller / eine bestimmte Aktion haben, sicherstellen müssen, dass auf diesem Controller / dieser Aktion 'erzwingenSsl' hinzugefügt wird.
Manuel Darveau
2
"In der folgenden Tabelle sind weitere kleine, aber wichtige Änderungen in Express 4 aufgeführt: ... Die Funktion app.configure () wurde entfernt. Verwenden Sie die Funktion process.env.NODE_ENV oder app.get ('env'), um die Umgebung und zu erkennen Konfigurieren Sie die App entsprechend. "
Kevin Wheeler
9
Beachten Sie außerdem, dass res.redirectdies standardmäßig eine 302-Umleitung ist (zumindest in Express 4.x). Aus SEO- und Caching-Gründen möchten Sie wahrscheinlich stattdessen eine 301-Weiterleitung. Ersetzen Sie die entsprechende Zeile durchreturn res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
Kevin Wheeler
6
Hinweis: Express 4.xEntfernen Sie in die app.configureLinie und verwenden Sie einfach den inneren Trank. app.configureist Legacy-Code und nicht mehr in Express enthalten.
Augie Gardner
96

Die Antwort ist, den Header von 'x-forwarded-proto' zu verwenden, den Heroku weiterleitet, während es sein Proxy-Dingamabob tut. (Randnotiz: Sie übergeben auch einige andere x-Variablen, die nützlich sein können, überprüfen Sie sie ).

Mein 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 */
})

Danke Brandon, ich habe nur auf diese 6-stündige Verspätung gewartet, bei der ich meine eigene Frage nicht beantworten konnte.

Derek Bredensteiner
quelle
4
Würde dies nicht andere Methoden als GETdurchlassen?
Jed Schmidt
1
@ Aaron: Nun, Sie würden möglicherweise Informationen verlieren, wenn Sie eine POST-Anfrage transparent umleiten würden. Ich denke, Sie sollten eine 400 auf andere Anfragen als GET für http zurückgeben.
Theodorton
3
Sie können ein && process.env.NODE_ENV === "production"in Ihre Bedingung werfen , wenn Sie nur möchten, dass es in Ihrer Produktionsumgebung funktioniert.
Keepitreal
307 (Umleitung mit derselben Methode) ist wahrscheinlich besser als ein 400-Fehler.
Beni Cherniavsky-Paskin
Es gibt mehrere Probleme mit dieser Antwort. Lesen Sie die nächste Antwort unten ( stackoverflow.com/a/23894573/14193 ) und bewerten Sie diese.
Neil
22

Die akzeptierte Antwort enthält eine fest codierte Domain. Dies ist nicht gut, wenn Sie denselben Code auf mehreren Domains haben (z. B. dev-yourapp.com, test-yourapp.com, yourapp.com).

Verwenden Sie stattdessen Folgendes:

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

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

Joan-Diego Rodriguez
quelle
Funktioniert gut. Ich duno , warum ich musste einfach ersetzen req.hostnamemit req.headers.hostvielleicht Express - Version ich bin in 4.2
Jeremy Piednoel
16

Ich habe ein kleines Knotenmodul geschrieben, das SSL für Expressprojekte erzwingt. Es funktioniert sowohl in Standardsituationen als auch bei Reverse-Proxys (Heroku, Nodejitsu usw.).

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

Florian
quelle
6

Wenn Sie den x-forwarded-protoHeader auf Ihrem lokalen Host testen möchten , können Sie mit nginx eine vhost-Datei einrichten, die alle Anforderungen an Ihre Knoten-App weiterleitet. Ihre nginx vhost-Konfigurationsdatei könnte folgendermaßen aussehen

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

Die wichtigen Punkte hierbei sind, dass Sie alle Anforderungen an den localhost-Port 3000 weiterleiten (hier wird Ihre Knoten-App ausgeführt) und eine Reihe von Headern einrichten, einschließlich X-Forwarded-Proto

Erkennen Sie dann in Ihrer App diesen Header wie gewohnt

ausdrücken

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

Gastgeber

Schließlich müssen Sie diese Zeile zu Ihrer hostsDatei hinzufügen

127.0.0.1 dummy.com
simo
quelle
6

Sie sollten einen Blick auf heroku-ssl-redirect werfen . Es wirkt wie ein Zauber!

var sslRedirect = require('heroku-ssl-redirect');
var express = require('express');
var app = express();

// enable ssl redirect
app.use(sslRedirect());

app.get('/', function(req, res){
  res.send('hello world');
});

app.listen(3000);
Julien Le Coupanec
quelle
4

Wenn Sie cloudflare.com als CDN in Kombination mit heroku verwenden, können Sie die automatische SSL-Umleitung innerhalb von Cloudflare auf folgende Weise aktivieren:

  1. Melden Sie sich an und gehen Sie zu Ihrem Dashboard

  2. Wählen Sie Seitenregeln

    Wählen Sie Seitenregeln

  3. Fügen Sie Ihre Domain hinzu, z. B. www.example.com, und wechseln Sie immer mit https zu on Schalten Sie immer https ein
Electronix384128
quelle
3

Loopback-Benutzer können eine leicht angepasste Version von arcseldon answer als Middleware verwenden:

server / middleware / forcessl.js

module.exports = function() {  
  return function forceSSL(req, res, next) {
    var FORCE_HTTPS = process.env.FORCE_HTTPS || false;
      if (req.headers['x-forwarded-proto'] !== 'https' && FORCE_HTTPS) {
        return res.redirect(['https://', req.get('Host'), req.url].join(''));
      }
      next();
    };
 };

server / server.js

var forceSSL = require('./middleware/forcessl.js');
app.use(forceSSL());
Bunker
quelle
2

Dies ist eine Express-spezifischere Methode, um dies zu tun.

app.enable('trust proxy');
app.use('*', (req, res, next) => {
  if (req.secure) {
    return next();
  }
  res.redirect(`https://${req.hostname}${req.url}`);
});
denixtry
quelle
0
app.all('*',function(req,res,next){
  if(req.headers['x-forwarded-proto']!='https') {
    res.redirect(`https://${req.get('host')}`+req.url);
  } else {
    next(); /* Continue to other routes if we're not redirecting */
  }
});
Chiedo
quelle
0

Mit app.use und dynamischer URL. Funktioniert sowohl lokal als auch auf Heroku für mich

app.use(function (req, res, next) {
  if (req.header('x-forwarded-proto') === 'http') {
    res.redirect(301, 'https://' + req.hostname + req.url);
    return
  }
  next()
});
Tuancharlie
quelle
-1

Das Überprüfen des Protokolls im X-Forwarded-Proto-Header funktioniert bei Heroku einwandfrei, genau wie Derek darauf hingewiesen hat. Für was es wert ist , hier ist ein Kern der Express Middleware , die ich benutze und seinen entsprechenden Tests.

Peter Marklund
quelle