Heroku NodeJS http para https ssl redirecionamento forçado

105

Eu tenho um aplicativo instalado e funcionando no heroku com express on node com https ,. Como faço para identificar o protocolo para forçar um redirecionamento para https com nodejs no heroku?

Meu aplicativo é apenas um servidor http simples, ele (ainda) não percebe que o heroku está enviando solicitações 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
fonte
6
O suporte do Heroku respondeu à minha pergunta acima, e ainda não o encontrei postado aqui, então pensei em postar em público e compartilhar o conhecimento. Eles passam muitas informações sobre a solicitação original com seus cabeçalhos de solicitação prefixados com um 'x-'. Aqui está o código que estou usando agora (no topo das minhas definições de rota):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 então eu entendo que você verifique se há https como este e redirecione, se necessário. Mas existe uma maneira de fazer o redirecionamento em nível de DNS com seu provedor de nome de domínio. Portanto, antes de o navegador resolver o DNS, ele já está em https. Porque, com essa abordagem, acho que, devido ao meu conhecimento sobre redirecionamentos, uma vez que a solicitação é feita por http e depois novamente por https. Portanto, se dados confidenciais foram enviados, eles foram enviados por http uma vez. em seguida, por https. O que meio que anula o propósito. Por favor, me avise se eu estiver errado.
Muhammad Umer
@MuhammadUmer, seu raciocínio parece estar no limite aqui, você já descobriu mais?
Karoh
Eu simplesmente usei o cloudflare como servidor de nomes que funciona como nginx e me permite redirecionar para a versão SSL apenas clicando no botão de alternância. você também pode fazer isso: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/… Além disso, normalmente ninguém envia dados imediatamente, eles geralmente chegam ao formulário e depois os enviam. portanto, em código do lado do servidor, servidor dns, cabeçalho http, javascript, você pode verificar e redirecionar para https developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
Muhammad Umer

Respostas:

107

A partir de hoje, 10 de outubro de 2014 , usando a pilha Heroku Cedar e ExpressJS ~ 3.4.4 , aqui está um conjunto de código de trabalho.

A principal coisa a lembrar aqui é que ESTAMOS implantando no Heroku. A terminação SSL ocorre no balanceador de carga, antes que o tráfego criptografado alcance seu aplicativo de nó. É possível testar se https foi usado para fazer a solicitação com req.headers ['x-forwarded-proto'] === 'https' .

Não precisamos nos preocupar em ter certificados SSL locais dentro do aplicativo, etc., como você faria se estivesse hospedando em outros ambientes. No entanto, você deve obter um complemento SSL aplicado por meio de complementos do Heroku primeiro, se estiver usando seu próprio certificado, subdomínios etc.

Em seguida, basta adicionar o seguinte para fazer o redirecionamento de qualquer coisa diferente de HTTPS para HTTPS. Isso está muito próximo da resposta aceita acima, mas:

  1. Garante que você use "app.use" (para todas as ações, não apenas para obter)
  2. Externiza explicitamente a lógica forceSsl em uma função declarada
  3. Não usa '*' com "app.use" - isso realmente falhou quando testei.
  4. Aqui, quero apenas SSL em produção. (Mude de acordo com suas necessidades)

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 usuários SailsJS (0.10.x). Você pode simplesmente criar uma política (enforceSsl.js) dentro de api / políticas:

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

Em seguida, faça referência a config / policies.js junto com quaisquer outras políticas, por exemplo:

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

Arceldon
fonte
1
Uma nota sobre o uso de uma política de velas: Conforme declarado em sailsjs.org/#/documentation/concepts/Policies : "Os mapeamentos de política padrão não" em cascata "ou" gotejam ". Os mapeamentos especificados para as ações do controlador substituirão o mapeamento padrão. " Isso significa que assim que você tiver outras políticas para um controlador / ação específica, você terá que adicionar 'enforceSsl' a esse controlador / ação.
Manuel Darveau
2
"A tabela a seguir lista outras alterações pequenas, mas importantes no Expresso 4: ... A função app.configure () foi removida. Use a função process.env.NODE_ENV ou app.get ('env') para detectar o ambiente e configure o aplicativo adequadamente. "
Kevin Wheeler
9
Além disso, observe que res.redirecto padrão é um redirecionamento 302 (pelo menos no expresso 4.x). Por motivos de SEO e armazenamento em cache, você provavelmente deseja um redirecionamento 301. Substitua a linha correspondente porreturn res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
Kevin Wheeler
6
Nota: Em Express 4.x, remova a app.configurelinha e use apenas a poção interna. app.configureé um código legado e não está mais incluído no expresso.
Augie Gardner
96

A resposta é usar o cabeçalho de 'x-forwarded-proto' que Heroku passa adiante enquanto faz seu proxy thingamabob. (observação lateral: eles passam várias outras variáveis ​​x também que podem ser úteis, verifique-as ).

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

Obrigado Brandon, estava apenas esperando aquele atraso de 6 horas que não me deixou responder minha própria pergunta.

Derek Bredensteiner
fonte
4
isso não deixaria outros métodos GETpassarem?
Jed Schmidt
1
@Aaron: Bem, você potencialmente perderia informações se redirecionasse de forma transparente uma solicitação POST. Acho que você deve retornar 400 em outras solicitações além de GET para http.
theodorton
3
Você pode lançar um && process.env.NODE_ENV === "production"em seu condicional se quiser apenas que funcione em seu ambiente de produção.
keepitreal
307 (redirecionar com o mesmo método) é provavelmente melhor do que um erro 400.
Beni Cherniavsky-Paskin
Existem vários problemas com esta resposta, consulte a próxima resposta abaixo ( stackoverflow.com/a/23894573/14193 ) e avalie este.
Neil
22

A resposta aceita contém um domínio codificado, o que não é muito bom se você tiver o mesmo código em vários domínios (por exemplo: dev-yourapp.com, test-yourapp.com, yourapp.com).

Em vez disso, use:

/* 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
fonte
Funciona bem. Eu entendi porque eu só tive que substituir req.hostnamepela req.headers.hostversão talvez expressa estou em 4.2
Jeremy Piednoel
16

Eu escrevi um pequeno módulo de nó que aplica SSL em projetos express. Funciona tanto em situações padrão quanto no caso de proxies reversos (Heroku, nodejitsu, etc.)

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

florian
fonte
6

Se quiser testar o x-forwarded-protocabeçalho em seu localhost, você pode usar nginx para configurar um arquivo vhost que faz proxy de todas as solicitações para seu aplicativo de nó. Seu arquivo de configuração nginx vhost pode ser parecido com este

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

O que é importante aqui é que você está fazendo proxy de todas as solicitações para localhost porta 3000 (é onde seu aplicativo de nó está sendo executado) e está configurando um monte de cabeçalhos, incluindo X-Forwarded-Proto

Então, em seu aplicativo, detecte esse cabeçalho como de costume

Expressar

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

Hosts

Finalmente você deve adicionar esta linha ao seu hostsarquivo

127.0.0.1 dummy.com
simo
fonte
6

Você deve dar uma olhada em heroku-ssl-redirect . Ele funciona como um encanto!

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

Se você estiver usando cloudflare.com como CDN em combinação com heroku, você pode habilitar o redirecionamento SSL automático dentro do cloudflare facilmente assim:

  1. Faça login e vá para o seu painel

  2. Selecione as regras da página

    Selecione as regras da página

  3. Adicione seu domínio, por exemplo, www.example.com e alterne sempre usar https para ativado Mude sempre usar https para ativado
electronix384128
fonte
3

Os usuários de loopback podem usar uma versão ligeiramente adaptada da resposta do arcseldon como middleware:

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
fonte
2

Esta é uma maneira específica mais expressa de fazer isso.

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

Com app.use e url dinâmico. Funciona localmente e no Heroku para mim

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

Verificar o protocolo no cabeçalho X-Forwarded-Proto funciona bem no Heroku, assim como Derek apontou. Para constar, aqui está uma essência do middleware Express que uso e seu teste correspondente.

Peter Marklund
fonte