Heroku NodeJS http เป็น https ssl บังคับให้เปลี่ยนเส้นทาง


105

ฉันมีแอปพลิเคชั่นที่ทำงานบน heroku ด้วยการแสดงบนโหนดด้วย https ,. ฉันจะระบุโปรโตคอลเพื่อบังคับให้เปลี่ยนเส้นทางไปยัง https ด้วย nodejs บน heroku ได้อย่างไร

แอปของฉันเป็นเพียงเซิร์ฟเวอร์ http ธรรมดา แต่ยังไม่ทราบว่า heroku กำลังส่งคำขอ https:

/* Heroku provides the port they want you on in this environment variable (hint: it's not 80) */
app.listen(process.env.PORT || 3000);

6
ฝ่ายสนับสนุนของ Heroku ตอบคำถามข้างต้นของฉันและฉันไม่พบว่ามีการโพสต์ไว้ที่นี่แล้วดังนั้นฉันจึงคิดว่าจะโพสต์ในที่สาธารณะและแบ่งปันความรู้ พวกเขาส่งข้อมูลจำนวนมากเกี่ยวกับคำขอเดิมโดยมีส่วนหัวของคำขอที่ขึ้นต้นด้วย 'x-' นี่คือรหัสที่ฉันใช้อยู่ตอนนี้ (ที่ด้านบนของคำจำกัดความเส้นทางของฉัน):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
โอเคฉันได้รับว่าคุณตรวจสอบ https แบบนี้และเปลี่ยนเส้นทางหากจำเป็น แต่มีวิธีเปลี่ยนเส้นทางที่ระดับ DNS กับผู้ให้บริการชื่อโดเมนของคุณหรือไม่ ดังนั้นก่อนที่เบราว์เซอร์จะแก้ไข DNS มันอยู่ที่ https แล้ว เนื่องจากด้วยวิธีนี้ฉันคิดว่าได้รับความรู้เกี่ยวกับการเปลี่ยนเส้นทางที่เมื่อมีการร้องขอผ่าน http แล้วอีกครั้งบน https ดังนั้นหากมีการส่งข้อมูลที่ละเอียดอ่อนข้อมูลนั้นจะถูกส่งผ่าน http หนึ่งครั้ง จากนั้นทับ https แบบไหนที่เอาชนะจุดประสงค์ โปรดแจ้งให้เราทราบหากฉันผิด
Muhammad Umer

@MuhammadUmer เหตุผลของคุณดูเหมือนอยู่ที่นี่คุณเคยค้นพบมากขึ้นหรือไม่?
Karoh

ฉันใช้ cloudflare เป็นเนมเซิร์ฟเวอร์ซึ่งทำงานเป็น nginx และให้ฉันเปลี่ยนเส้นทางไปยังเวอร์ชัน ssl เพียงแค่คลิกปุ่มสลับ คุณสามารถทำได้เช่นกัน: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/… นอกจากนี้โดยปกติแล้วไม่มีใครส่งข้อมูลทันทีที่พวกเขามักจะเข้าสู่แบบฟอร์มแล้วส่ง ดังนั้นในโค้ดฝั่งเซิร์ฟเวอร์, เซิร์ฟเวอร์ DNS, ส่วนหัว http, จาวาสคริปต์คุณสามารถตรวจสอบและเปลี่ยนเส้นทางไปยัง https developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
Muhammad Umer

คำตอบ:


107

ณ วันนี้10 ตุลาคม 2014โดยใช้Heroku Cedar stackและ ExpressJS ~ 3.4.4นี่คือชุดโค้ดที่ใช้งานได้

สิ่งสำคัญที่ต้องจำไว้ที่นี่คือเรากำลังปรับใช้กับ Heroku การยุติ SSL เกิดขึ้นที่ตัวจัดสรรภาระงานก่อนที่การรับส่งข้อมูลที่เข้ารหัสจะไปถึงแอปโหนดของคุณ เป็นไปได้ที่จะทดสอบว่าใช้ https เพื่อสร้างคำขอด้วยreq.headers ['x-forwarded-proto'] === 'https'หรือไม่

เราไม่จำเป็นต้องกังวลเกี่ยวกับการมีใบรับรอง SSL ในเครื่องภายในแอป ฯลฯ เช่นเดียวกับที่คุณอาจโฮสต์ในสภาพแวดล้อมอื่น ๆ อย่างไรก็ตามคุณควรได้รับ SSL Add-On ที่ใช้ผ่าน Heroku Add-ons ก่อนหากใช้ใบรับรองของคุณเองโดเมนย่อยและอื่น ๆ

จากนั้นเพิ่มสิ่งต่อไปนี้เพื่อทำการเปลี่ยนเส้นทางจากสิ่งอื่นที่ไม่ใช่ HTTPS ไปยัง HTTPS นี่ใกล้เคียงกับคำตอบที่ยอมรับข้างต้นมาก แต่:

  1. ตรวจสอบให้แน่ใจว่าคุณใช้ "app.use" (สำหรับการกระทำทั้งหมดไม่ใช่แค่รับ)
  2. ภายนอกลอจิก forceSsl อย่างชัดเจนให้เป็นฟังก์ชันที่ประกาศ
  3. ไม่ได้ใช้ '*' กับ "app.use" - สิ่งนี้ล้มเหลวจริง ๆ เมื่อฉันทดสอบ
  4. ที่นี่ฉันต้องการ SSL ในการผลิตเท่านั้น (เปลี่ยนตามความต้องการของคุณ)

รหัส:

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

หมายเหตุสำหรับผู้ใช้ SailsJS (0.10.x) คุณสามารถสร้างนโยบาย (enforceSsl.js) ภายใน api / นโยบาย:

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

จากนั้นอ้างอิงจาก config / policy.js พร้อมกับนโยบายอื่น ๆ เช่น:

'*': ['authenticated', 'enforceSsl']


1
หมายเหตุเกี่ยวกับการใช้นโยบายใบเรือ: ตามที่ระบุไว้ในsailsjs.org/#/documentation/concepts/Policies : "การแมปนโยบายเริ่มต้นจะไม่" เรียงซ้อน "หรือ" หยดลง "การแมปที่ระบุสำหรับการดำเนินการของคอนโทรลเลอร์จะแทนที่การแมปเริ่มต้น " ซึ่งหมายความว่าทันทีที่คุณมีนโยบายอื่น ๆ สำหรับตัวควบคุม / การดำเนินการเฉพาะคุณจะต้องเพิ่ม 'enforceSsl' ในตัวควบคุม / การดำเนินการเหล่านั้น
Manuel Darveau

2
"ตารางต่อไปนี้แสดงรายการการเปลี่ยนแปลงเล็ก ๆ แต่สำคัญอื่น ๆ ใน Express 4: ... ฟังก์ชัน app.configure () ถูกลบออกใช้ฟังก์ชัน process.env.NODE_ENV หรือ app.get ('env') เพื่อตรวจจับสภาพแวดล้อมและ กำหนดค่าแอปให้สอดคล้องกัน "
Kevin Wheeler

9
นอกจากนี้โปรดทราบว่าres.redirectค่านี้เป็นค่าเริ่มต้นของการเปลี่ยนเส้นทาง 302 (อย่างน้อยใน express 4.x) ด้วยเหตุผลด้าน SEO และการแคชคุณอาจต้องการเปลี่ยนเส้นทาง 301 แทน แทนที่บรรทัดที่เกี่ยวข้องด้วยreturn res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
Kevin Wheeler

6
หมายเหตุ: ในExpress 4.xลบapp.configureเส้นและใช้ยาชั้นใน app.configureเป็นรหัสเดิมและไม่รวมอยู่ใน Express อีกต่อไป
Augie Gardner

96

คำตอบคือใช้ส่วนหัวของ 'x-forwarded-proto' ที่ Heroku ส่งไปข้างหน้าเช่นเดียวกับที่เป็น proxy thingamabob (หมายเหตุด้านข้าง: พวกเขาส่งผ่านตัวแปร x- อื่น ๆ อีกหลายตัวด้วยซึ่งอาจเป็นประโยชน์ลองดู )

รหัสของฉัน:

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

ขอบคุณแบรนดอนแค่รอสิ่งที่ล่าช้าไป 6 ชั่วโมงซึ่งจะไม่ให้ฉันตอบคำถามของตัวเอง


4
สิ่งนี้จะไม่ปล่อยให้วิธีอื่นนอกจากGETผ่าน?
Jed Schmidt

1
@Aaron: คุณอาจสูญเสียข้อมูลหากคุณเปลี่ยนเส้นทางคำขอ POST อย่างโปร่งใส ฉันคิดว่าคุณควรส่งคืน 400 สำหรับคำขออื่นที่ไม่ใช่ GET สำหรับ http
theodorton

3
คุณอาจ&& process.env.NODE_ENV === "production"ใส่เงื่อนไขของคุณได้ถ้าคุณต้องการให้มันทำงานในสภาพแวดล้อมการผลิตของคุณเท่านั้น
keepitreal

307 (เปลี่ยนเส้นทางด้วยวิธีการเดียวกัน) น่าจะดีกว่าข้อผิดพลาด 400
Beni Cherniavsky-Paskin

มีปัญหาหลายประการเกี่ยวกับคำตอบนี้โปรดดูคำตอบถัดไปด้านล่าง ( stackoverflow.com/a/23894573/14193 ) และให้คะแนนข้อนี้ลดลง
Neil

22

คำตอบที่ยอมรับนั้นมีโดเมนแบบฮาร์ดโค้ดซึ่งไม่ดีเกินไปหากคุณมีรหัสเดียวกันในหลายโดเมน (เช่น dev-yourapp.com, test-yourapp.com, yourapp.com)

ใช้สิ่งนี้แทน:

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


ใช้งานได้ดี ฉันไม่รู้ว่าทำไมฉันต้องแทนที่req.hostnameด้วยreq.headers.hostเวอร์ชันด่วนที่ฉันอยู่ใน 4.2
Jeremy Piednoel


6

หากคุณต้องการทดสอบx-forwarded-protoส่วนหัวบนโลคัลโฮสต์ของคุณคุณสามารถใช้nginxเพื่อตั้งค่าไฟล์ vhost ที่มอบฉันทะคำขอทั้งหมดไปยังแอปโหนดของคุณ ไฟล์กำหนดค่า nginx vhost ของคุณอาจมีลักษณะเช่นนี้

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

บิตที่สำคัญที่นี่คือคุณกำลังพร็อกซีคำขอทั้งหมดไปยัง localhost port 3000 (นี่คือที่ที่แอปโหนดของคุณกำลังทำงานอยู่) และคุณกำลังตั้งค่าส่วนหัวจำนวนมากรวมถึง X-Forwarded-Proto

จากนั้นในแอปของคุณจะตรวจพบส่วนหัวนั้นตามปกติ

ด่วน

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

โคอา

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ไฟล์ของคุณ

127.0.0.1 dummy.com


4

หากคุณใช้ cloudflare.com เป็น CDN ร่วมกับ heroku คุณสามารถเปิดใช้งานการเปลี่ยนเส้นทาง ssl อัตโนมัติภายใน cloudflare ได้อย่างง่ายดายดังนี้:

  1. เข้าสู่ระบบและไปที่แดชบอร์ดของคุณ

  2. เลือกกฎของเพจ

    เลือกกฎของเพจ

  3. เพิ่มโดเมนของคุณเช่น www.example.com และเปลี่ยนใช้ https เป็นเปิดเสมอ สลับใช้ https เป็นเปิดเสมอ

3

ผู้ใช้ Loopback สามารถใช้คำตอบ arcseldon เวอร์ชันดัดแปลงเล็กน้อยเป็นมิดเดิลแวร์:

เซิร์ฟเวอร์ / มิดเดิลแวร์ / 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.js

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

2

นี่เป็นวิธีที่เฉพาะเจาะจงมากขึ้นในการดำเนินการของ Express

app.enable('trust proxy');
app.use('*', (req, res, next) => {
  if (req.secure) {
    return next();
  }
  res.redirect(`https://${req.hostname}${req.url}`);
});

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

0

ด้วย app.use และไดนามิก url ใช้ได้ทั้งในท้องถิ่นและใน Heroku สำหรับฉัน

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

-1

การตรวจสอบโปรโตคอลในส่วนหัว X-Forwarded-Proto ทำงานได้ดีบน Heroku เช่นเดียวกับที่ Derek ได้ชี้ให้เห็น สำหรับสิ่งที่คุ้มค่านี่คือส่วนสำคัญของมิดเดิลแวร์ Express ที่ฉันใช้และการทดสอบที่เกี่ยวข้อง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.