Heroku NodeJS http a https ssl redireccionamiento forzado

105

Tengo una aplicación en funcionamiento en heroku con express en el nodo con https. ¿Cómo identifico el protocolo para forzar una redirección a https con nodejs en heroku?

Mi aplicación es solo un servidor http simple, (todavía) no se da cuenta de que heroku le está enviando solicitudes https:

/* 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
fuente
6
El soporte de Heroku respondió a mi pregunta anterior, y no lo encontré publicado aquí ya, así que pensé en publicarlo en público y compartir el conocimiento. Pasan mucha información sobre la solicitud original con sus encabezados de solicitud prefijados con una 'x-'. Aquí está el código que estoy usando ahora (en la parte superior de mis definiciones de ruta):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, entonces entiendo que verifique https como este y redirija si es necesario. Pero, ¿hay alguna manera de realizar una redirección a nivel dns con su proveedor de nombre de dominio? Entonces, antes de que el navegador resuelva el DNS, ya está en https. Porque con este enfoque es que, creo que dado mi conocimiento de las redirecciones, una vez que la solicitud se realiza a través de http y luego nuevamente a través de https. Entonces, si se enviaron datos confidenciales, se enviaron a través de http una vez. luego sobre https. Lo que frustra un poco el propósito. Por favor, avíseme si me equivoco.
Muhammad Umer
@MuhammadUmer, su razonamiento parece acertado aquí, ¿alguna vez descubrió más?
Karoh
Simplemente utilicé cloudflare como servidor de nombres que funciona como nginx, y me permite redirigir a la versión ssl con solo hacer clic en el botón de alternancia. también puede hacer esto: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/… Además, generalmente nadie envía datos de inmediato, generalmente aterrizan en el formulario y luego los envían. Entonces, en el código del lado del servidor, servidor dns, encabezado http, javascript, puede verificar y redirigir a https developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
Muhammad Umer

Respuestas:

107

A partir de hoy, 10 de octubre de 2014 , usando la pila Heroku Cedar y ExpressJS ~ 3.4.4 , aquí hay un conjunto de código de trabajo.

Lo principal para recordar aquí es que ESTAMOS implementando en Heroku. La terminación de SSL ocurre en el balanceador de carga, antes de que el tráfico cifrado llegue a su aplicación de nodo. Es posible probar si se utilizó https para realizar la solicitud con req.headers ['x-fordered-proto'] === 'https' .

No es necesario que nos preocupemos por tener certificados SSL locales dentro de la aplicación, etc., como lo haría si estuviera alojando en otros entornos. Sin embargo, primero debe aplicar un complemento SSL a través de los complementos de Heroku si usa su propio certificado, subdominios, etc.

Luego, simplemente agregue lo siguiente para hacer la redirección de cualquier cosa que no sea HTTPS a HTTPS. Esto está muy cerca de la respuesta aceptada anterior, pero:

  1. Garantiza que utilice "app.use" (para todas las acciones, no solo para obtener)
  2. Externaliza explícitamente la lógica forceSsl en una función declarada
  3. No usa '*' con "app.use" - esto realmente falló cuando lo probé.
  4. Aquí, solo quiero SSL en producción. (Cambie según sus necesidades)

Código:

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

Nota para los usuarios de SailsJS (0.10.x). Simplemente puede crear una política (enforceSsl.js) dentro de api / policies:

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

Luego, haga referencia a config / policies.js junto con cualquier otra política, por ejemplo:

'*': ['autenticado', 'enforceSsl']

arcseldon
fuente
1
Una nota sobre el uso de una política de velas: Como se indica en sailsjs.org/#/documentation/concepts/Policies : "Las asignaciones de políticas predeterminadas no se" caen en cascada "ni se" filtran ". Las asignaciones especificadas para las acciones del controlador anularán la asignación predeterminada. " Esto significa que tan pronto como tenga otras políticas para un controlador / acción específicos, deberá asegurarse de agregar 'enforceSsl' en ese controlador / acción.
Manuel Darveau
2
"La siguiente tabla enumera otros cambios pequeños pero importantes en Express 4: ... La función app.configure () ha sido eliminada. Utilice la función process.env.NODE_ENV o app.get ('env') para detectar el entorno y configure la aplicación en consecuencia ".
Kevin Wheeler
9
Además, tenga en cuenta que res.redirectesto tiene como valor predeterminado una redirección 302 (al menos en Express 4.x). Por motivos de SEO y almacenamiento en caché, probablemente desee una redirección 301 en su lugar. Reemplace la línea correspondiente conreturn res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
Kevin Wheeler
6
Nota: adentro Express 4.x, elimine la app.configurelínea y solo use la poción interna. app.configurees un código heredado y ya no se incluye en Express.
Augie Gardner
96

La respuesta es usar el encabezado de 'x-fordered-proto' que Heroku transmite como lo hace con su proxy thingamabob. (nota al margen: también pasan varias otras variables x que pueden ser útiles, compruébalo )

Mi código:

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

Gracias Brandon, estaba esperando ese retraso de 6 horas que no me dejaba responder mi propia pregunta.

Derek Bredensteiner
fuente
4
¿No dejaría esto otros métodos que no fueran a GETtravés?
Jed Schmidt
1
@Aaron: Bueno, podría perder información si redirigiera de forma transparente una solicitud POST. Creo que debería devolver un 400 en otras solicitudes que no sean GET para http.
theodorton
3
Puede agregar un && process.env.NODE_ENV === "production"a su condicional si solo desea que funcione en su entorno de producción.
keepitreal
307 (redireccionar con el mismo método) es probablemente mejor que un error 400.
Beni Cherniavsky-Paskin
Hay varios problemas con esta respuesta, consulte la siguiente respuesta a continuación ( stackoverflow.com/a/23894573/14193 ) y califíquela .
Neil
22

La respuesta aceptada tiene un dominio codificado, lo cual no es demasiado bueno si tiene el mismo código en varios dominios (por ejemplo: dev-yourapp.com, test-yourapp.com, yourapp.com).

Use esto en su lugar:

/* 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 Rodríguez
fuente
Funciona bien. No sé por qué solo tuve que reemplazar req.hostnamecon la req.headers.hostversión quizás expresa estoy en 4.2
Jeremy Piednoel
6

Si desea probar el x-forwarded-protoencabezado en su localhost, puede usar nginx para configurar un archivo vhost que envía todas las solicitudes a su aplicación de nodo. Su archivo de configuración de nginx vhost podría verse así

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

Lo importante aquí es que está enviando todas las solicitudes al puerto localhost 3000 (aquí es donde se ejecuta su aplicación de nodo) y está configurando un montón de encabezados que incluyen X-Forwarded-Proto

Luego, en su aplicación, detecte ese encabezado como de costumbre

Rápido

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

Hospedadores

Finalmente tienes que agregar esta línea a tu hostsarchivo

127.0.0.1 dummy.com
simo
fuente
6

Debería echar un vistazo a heroku-ssl-redirect . ¡Funciona a las mil maravillas!

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
fuente
4

Si está utilizando cloudflare.com como CDN en combinación con heroku, puede habilitar la redirección automática de SSL dentro de cloudflare fácilmente de esta manera:

  1. Inicie sesión y vaya a su panel

  2. Seleccionar reglas de página

    Seleccionar reglas de página

  3. Agregue su dominio, por ejemplo, www.example.com y cambie usar siempre https a activado Activar siempre usar https
electronix384128
fuente
3

Los usuarios de loopback pueden usar una versión ligeramente adaptada de la respuesta de arcseldon como middleware:

servidor / 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());
Búnker
fuente
2

Esta es una forma más específica de Express para hacer esto.

app.enable('trust proxy');
app.use('*', (req, res, next) => {
  if (req.secure) {
    return next();
  }
  res.redirect(`https://${req.hostname}${req.url}`);
});
denixtry
fuente
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
fuente
0

Con app.use y url dinámica. Funciona tanto localmente como en Heroku para mí

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

Verificar el protocolo en el encabezado X-Fordered-Proto funciona bien en Heroku, tal como lo ha señalado Derek. Por lo que vale, aquí hay una idea general del middleware Express que utilizo y su prueba correspondiente.

Peter Marklund
fuente