Heroku NodeJS http ke https ssl pengalihan paksa

105

Saya memiliki aplikasi yang aktif dan berjalan di heroku dengan express on node dengan https ,. Bagaimana cara mengidentifikasi protokol untuk memaksa pengalihan ke https dengan nodejs di heroku?

Aplikasi saya hanyalah server http sederhana, ia (belum) menyadari bahwa heroku mengirimkannya permintaan 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
sumber
6
Dukungan Heroku menjawab pertanyaan saya di atas, dan saya belum menemukannya diposting di sini, jadi saya pikir saya akan mempostingnya di depan umum dan berbagi pengetahuan. Mereka menyampaikan banyak info tentang permintaan asli dengan header permintaannya yang diawali dengan 'x-'. Berikut kode yang saya gunakan sekarang (di bagian atas definisi rute saya):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 jadi saya mengerti bahwa Anda memeriksa https seperti ini dan mengarahkan ulang jika diperlukan. Tapi apakah ada cara untuk melakukan reroute di level dns dengan penyedia nama domain Anda. Jadi sebelum browser menyelesaikan DNS, itu sudah di https. Karena dengan pendekatan ini, saya pikir mengingat pengetahuan saya tentang pengalihan, bahwa permintaan sekali dibuat melalui http dan kemudian lagi melalui https. Jadi jika data sensitif dikirim maka itu dikirim melalui http sekali. lalu melalui https. Yang agak mengalahkan tujuannya. Tolong beritahu saya jika saya salah.
Muhammad Umer
@MuhammadUmer, penalaran kalian sepertinya tepat di sini, pernahkah kalian menemukan lebih banyak?
Karoh
Saya hanya menggunakan cloudflare sebagai nameserver yang berfungsi sebagai nginx, dan memungkinkan saya mengalihkan ke versi ssl hanya dengan mengklik tombol sakelar. Anda juga dapat melakukan ini: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/… Selain itu, biasanya tidak ada yang langsung mengirimkan data mereka biasanya masuk ke formulir dan kemudian mengirimkannya. jadi pada kode sisi server, server dns, header http, javascript Anda dapat memeriksa dan mengarahkan ke https developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
Muhammad Umer

Jawaban:

107

Mulai hari ini, 10 Oktober 2014 , menggunakan tumpukan Heroku Cedar , dan ExpressJS ~ 3.4.4 , berikut adalah satu set kode yang berfungsi.

Hal utama yang perlu diingat di sini adalah bahwa kami SUDAH menerapkan ke Heroku. Penghentian SSL terjadi di penyeimbang beban, sebelum lalu lintas terenkripsi mencapai aplikasi node Anda. Anda dapat menguji apakah https digunakan untuk membuat permintaan dengan req.headers ['x-forwarded-proto'] === 'https' .

Kami tidak perlu khawatir dengan memiliki sertifikat SSL lokal di dalam aplikasi dll seperti yang mungkin Anda lakukan jika menghosting di lingkungan lain. Namun, Anda harus mendapatkan Add-On SSL yang diterapkan melalui Add-on Heroku terlebih dahulu jika menggunakan sertifikat, sub-domain Anda sendiri, dll.

Kemudian tambahkan yang berikut ini untuk melakukan pengalihan dari selain HTTPS ke HTTPS. Ini sangat dekat dengan jawaban yang diterima di atas, tetapi:

  1. Pastikan Anda menggunakan "app.use" (untuk semua tindakan, tidak hanya mendapatkan)
  2. Secara eksplisit mengeluarkan logika forceSsl ke dalam fungsi yang dideklarasikan
  3. Tidak menggunakan '*' dengan "app.use" - ini sebenarnya gagal saat saya mengujinya.
  4. Di sini, saya hanya ingin SSL dalam produksi. (Ubah sesuai kebutuhan Anda)

Kode:

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

Catatan untuk pengguna SailsJS (0.10.x). Anda cukup membuat kebijakan (menegakkanSsl.js) di dalam api / kebijakan:

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

Kemudian referensi dari config / policy.js bersama dengan kebijakan lainnya, misalnya:

'*': ['Authenticated', 'EnegceSsl']

arcseldon.dll
sumber
1
Catatan tentang penggunaan kebijakan layar: Seperti yang dinyatakan dalam sailsjs.org/#/documentation/concepts/Policies : "Pemetaan kebijakan default tidak" menurun "atau" menetes ke bawah ". Pemetaan yang ditentukan untuk tindakan pengontrol akan menggantikan pemetaan default. " Ini berarti bahwa segera setelah Anda memiliki kebijakan lain untuk pengontrol / tindakan tertentu, Anda harus memastikan untuk menambahkan 'forcedSsl' pada pengontrol / tindakan tersebut.
Manuel Darveau
2
"Tabel berikut mencantumkan perubahan kecil namun penting lainnya di Express 4: ... Fungsi app.configure () telah dihapus. Gunakan fungsi process.env.NODE_ENV atau app.get ('env') untuk mendeteksi lingkungan dan konfigurasikan aplikasi sesuai. "
Kevin Wheeler
9
Selain itu, perhatikan bahwa res.redirectdefault ini adalah pengalihan 302 (setidaknya dalam ekspres 4.x). Untuk alasan SEO dan cache, Anda mungkin menginginkan pengalihan 301. Ganti baris yang sesuai denganreturn res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
Kevin Wheeler
6
Catatan: Masuk Express 4.x, hapus app.configuregaris dan cukup gunakan ramuan bagian dalam. app.configureadalah kode warisan dan tidak lagi disertakan dalam ekspres.
Augie Gardner
96

Jawabannya adalah dengan menggunakan tajuk 'x-forwarded-proto' yang diteruskan Heroku seperti halnya proxy thingamabob. (catatan tambahan: Mereka melewatkan beberapa variabel x lain juga yang mungkin berguna, periksalah ).

Kode saya:

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

Terima kasih Brandon, hanya menunggu penundaan 6 jam yang tidak akan membiarkan saya menjawab pertanyaan saya sendiri.

Derek Bredensteiner
sumber
4
bukankah ini akan membiarkan metode lain selain GETmelalui?
Jed Schmidt
1
@ Aaron: Nah, Anda berpotensi kehilangan informasi jika Anda secara transparan mengarahkan ulang permintaan POST. Saya pikir Anda harus mengembalikan 400 permintaan selain GET untuk http.
theodorton
3
Anda mungkin memasukkan && process.env.NODE_ENV === "production"persyaratan jika Anda hanya ingin itu berfungsi di lingkungan produksi Anda.
keepitreal
307 (redirect dengan metode yang sama) mungkin lebih baik daripada error 400.
Beni Cherniavsky-Paskin
Ada beberapa masalah dengan jawaban ini, lihat jawaban berikutnya di bawah ( stackoverflow.com/a/23894573/14193 ) dan beri peringkat yang ini.
Neil
22

Jawaban yang diterima memiliki domain hardcode di dalamnya, yang tidak terlalu bagus jika Anda memiliki kode yang sama di beberapa domain (misalnya: dev-yourapp.com, test-yourapp.com, yourapp.com).

Gunakan ini sebagai gantinya:

/* 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
sumber
Bekerja dengan baik. Saya tidak tahu mengapa saya harus mengganti req.hostnamedengan req.headers.hostmungkin versi ekspres. Saya di 4.2
Jeremy Piednoel
16

Saya telah menulis modul node kecil yang memberlakukan SSL pada proyek ekspres. Ini berfungsi baik dalam situasi standar dan dalam kasus proxy terbalik (Heroku, nodejitsu, dll.)

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

florian
sumber
6

Jika Anda ingin menguji x-forwarded-protoheader di localhost Anda, Anda dapat menggunakan nginx untuk menyiapkan file vhost yang memproksikan semua permintaan ke aplikasi node Anda. File konfigurasi nginx vhost Anda mungkin terlihat seperti ini

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

Bit penting di sini adalah Anda mem-proxy semua permintaan ke localhost port 3000 (ini adalah tempat aplikasi node Anda berjalan) dan Anda menyiapkan banyak header termasuk X-Forwarded-Proto

Kemudian di aplikasi Anda, deteksi header itu seperti biasa

Mengekspresikan

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

Tuan rumah

Akhirnya Anda harus menambahkan baris ini ke hostsfile Anda

127.0.0.1 dummy.com
simo
sumber
6

Anda harus melihat heroku-ssl-redirect . Ini bekerja seperti pesona!

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

Jika Anda menggunakan cloudflare.com sebagai CDN dalam kombinasi dengan heroku, Anda dapat mengaktifkan pengalihan ssl otomatis dalam cloudflare dengan mudah seperti ini:

  1. Masuk dan pergi ke dasbor Anda

  2. Pilih Aturan Halaman

    Pilih Aturan Halaman

  3. Tambahkan domain Anda, misalnya www.example.com dan alihkan selalu gunakan https ke aktif Alihkan selalu gunakan https ke aktif
electronix384128
sumber
3

Pengguna loopback dapat menggunakan versi jawaban arcseldon yang sedikit diadaptasi sebagai 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
sumber
2

Ini adalah cara yang lebih spesifik Express untuk melakukan ini.

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

Dengan app.use dan url dinamis. Bekerja baik secara lokal maupun di Heroku untuk saya

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

Memeriksa protokol di header X-Forwarded-Proto berfungsi dengan baik di Heroku, seperti yang ditunjukkan Derek. Untuk mengetahui manfaatnya, berikut adalah inti dari middleware Express yang saya gunakan dan pengujian yang sesuai.

Peter Marklund
sumber