Heroku NodeJS http đến https ssl buộc chuyển hướng


105

Tôi có một ứng dụng đang chạy trên heroku với express on node với https,. Làm cách nào để xác định giao thức buộc chuyển hướng đến https với nodejs trên heroku?

Ứng dụng của tôi chỉ là một máy chủ http đơn giản, nó không (chưa) nhận ra rằng heroku đang gửi cho nó các yêu cầu 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
Bộ phận hỗ trợ của Heroku đã trả lời câu hỏi ở trên của tôi và tôi chưa thấy nó được đăng ở đây, vì vậy tôi nghĩ rằng tôi sẽ đăng nó công khai và chia sẻ kiến ​​thức. Họ chuyển rất nhiều thông tin về yêu cầu ban đầu với tiêu đề yêu cầu của nó có tiền tố là 'x-'. Dưới đây là đoạn code tôi đang sử dụng bây giờ (ở trên cùng của định nghĩa con đường của tôi):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 nên tôi hiểu rằng bạn kiểm tra https như thế này và chuyển hướng nếu cần. Nhưng có cách nào để định tuyến lại ở cấp dns với nhà cung cấp tên miền của bạn. Vì vậy, trước khi trình duyệt phân giải DNS, nó đã có ở https. Bởi vì với cách tiếp cận này, tôi nghĩ với kiến ​​thức của mình về chuyển hướng, một khi yêu cầu được thực hiện qua http và sau đó lại qua https. Vì vậy, nếu dữ liệu nhạy cảm đã được gửi thì nó đã được gửi qua http một lần. sau đó qua https. Loại nào đánh bại mục đích. Xin vui lòng cho tôi biết nếu tôi sai.
Muhammad Umer

@MuhammadUmer, lý luận của bạn có vẻ khá khó hiểu ở đây, bạn đã bao giờ khám phá thêm chưa?
Karoh

tôi chỉ sử dụng cloudflare làm máy chủ định danh hoạt động như nginx và cho phép tôi chuyển hướng đến phiên bản ssl chỉ bằng cách nhấp vào nút bật tắt. bạn cũng có thể làm điều này: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/… Hơn nữa, thường không ai gửi dữ liệu ngay lập tức mà họ thường truy cập vào biểu mẫu và sau đó gửi. vân vân mã phía máy chủ, máy chủ dns, http header, javascript bạn có thể kiểm tra và chuyển hướng đến https developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
Muhammad Umer

Câu trả lời:


107

Kể từ hôm nay, ngày 10 tháng 10 năm 2014 , bằng cách sử dụng ngăn xếp Heroku CedarExpressJS ~ 3.4.4 , đây là một bộ mã hoạt động.

Điều chính cần nhớ ở đây là chúng tôi đang triển khai cho Heroku. Việc chấm dứt SSL xảy ra ở bộ cân bằng tải, trước khi lưu lượng được mã hóa đến ứng dụng nút của bạn. Có thể kiểm tra xem https có được sử dụng để thực hiện yêu cầu với req.headers ['x-forwarded-proto'] === 'https' hay không .

Chúng tôi không cần quan tâm đến việc có chứng chỉ SSL cục bộ bên trong ứng dụng, v.v. như bạn có thể làm nếu lưu trữ trong các môi trường khác. Tuy nhiên, bạn nên áp dụng Tiện ích bổ sung SSL qua Tiện ích bổ sung Heroku trước nếu sử dụng chứng chỉ của riêng bạn, các miền phụ, v.v.

Sau đó, chỉ cần thêm phần sau để thực hiện chuyển hướng từ bất kỳ thứ gì khác ngoài HTTPS sang HTTPS. Điều này rất gần với câu trả lời được chấp nhận ở trên, nhưng:

  1. Đảm bảo bạn sử dụng "app.use" (cho tất cả các hành động, không chỉ nhận)
  2. Rõ ràng bên ngoài logic forceSsl thành một hàm đã khai báo
  3. Không sử dụng '*' với "app.use" - điều này thực sự không thành công khi tôi thử nghiệm.
  4. Ở đây, tôi chỉ muốn SSL trong sản xuất. (Thay đổi theo nhu cầu của bạn)

Mã:

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

Lưu ý cho người dùng SailsJS (0.10.x). Bạn có thể chỉ cần tạo một chính sách (executeSsl.js) bên trong api / policy:

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

Sau đó, tham chiếu từ config / policy.js cùng với bất kỳ chính sách nào khác, ví dụ:

'*': ['xác thực', 'cưỡng chế']


1
Lưu ý về việc sử dụng chính sách buồm: Như đã nêu trong sailsjs.org/#/documentation/concept/Policies : "Ánh xạ chính sách mặc định không" thác "hoặc" nhỏ giọt ". Các ánh xạ chỉ định cho các hành động của bộ điều khiển sẽ ghi đè lên ánh xạ mặc định. " Điều này có nghĩa là ngay sau khi bạn có các chính sách khác cho bộ điều khiển / hành động cụ thể, bạn sẽ phải đảm bảo thêm 'executeSsl' trên bộ điều khiển / hành động đó.
Manuel Darveau

2
"Bảng sau liệt kê những thay đổi nhỏ nhưng quan trọng khác trong Express 4: ... Hàm app.configure () đã bị xóa. Sử dụng hàm process.env.NODE_ENV hoặc app.get ('env') để phát hiện môi trường và cấu hình ứng dụng phù hợp ".
Kevin Wheeler

9
Ngoài ra, lưu ý rằng res.redirectđiều này mặc định là chuyển hướng 302 (ít nhất là trong express 4.x). Vì lý do SEO và bộ nhớ đệm, bạn có thể muốn chuyển hướng 301. Thay thế dòng tương ứng vớireturn res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
Kevin Wheeler

6
Lưu ý: Trong Express 4.x, bỏ app.configuredòng và chỉ sử dụng lọ thuốc bên trong. app.configurelà mã kế thừa và không còn được đưa vào express.
Augie Gardner

96

Câu trả lời là sử dụng tiêu đề 'x-forwarded-proto' mà Heroku chuyển tiếp vì nó làm điều đó proxy. (lưu ý bên: Chúng cũng truyền một số biến x- ​​khác có thể hữu ích, hãy kiểm tra chúng ).

Mã của tôi:

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

Cảm ơn Brandon, tôi đã chờ đợi điều trì hoãn 6 giờ đó sẽ không cho phép tôi trả lời câu hỏi của riêng mình.


4
Điều này sẽ không cho phép các phương pháp khác hơn là GETthông qua?
Jed Schmidt

1
@Aaron: Chà, bạn có khả năng bị mất thông tin nếu bạn chuyển hướng một cách rõ ràng một yêu cầu ĐĂNG. Tôi nghĩ bạn nên trả lại 400 cho các yêu cầu khác ngoài GET cho http.
theodorton

3
Bạn có thể ném && process.env.NODE_ENV === "production"vào điều kiện của mình nếu bạn chỉ muốn nó hoạt động trong môi trường sản xuất của bạn.
keepitreal

307 (chuyển hướng với cùng một phương pháp) có lẽ tốt hơn lỗi 400.
Beni Cherniavsky-Paskin

Có nhiều vấn đề với câu trả lời này, hãy xem câu trả lời tiếp theo bên dưới ( stackoverflow.com/a/23894573/14193 ) và xếp hạng câu trả lời này xuống.
Neil

22

Câu trả lời được chấp nhận có một miền mã hóa cứng trong đó, điều này không quá tốt nếu bạn có cùng một mã trên một số miền (ví dụ: dev-yourapp.com, test-yourapp.com, yourapp.com).

Sử dụng cái này thay thế:

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


Hoạt động tốt. Tôi duno lý do tại sao tôi chỉ cần phải thay thế req.hostnamebằng req.headers.hostphiên bản có thể nhanh tôi trong 4,2
Jeremy Piednoel


6

Nếu bạn muốn kiểm tra x-forwarded-prototiêu đề trên máy chủ cục bộ của mình, bạn có thể sử dụng nginx để thiết lập tệp vhost ủy thác tất cả các yêu cầu tới ứng dụng nút của bạn. Tệp cấu hình nginx vhost của bạn có thể trông giống như thế này

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

Điều quan trọng ở đây là bạn đang ủy quyền tất cả các yêu cầu đến cổng localhost 3000 (đây là nơi ứng dụng nút của bạn đang chạy) và bạn đang thiết lập một loạt các tiêu đề bao gồm X-Forwarded-Proto

Sau đó, trong ứng dụng của bạn, phát hiện tiêu đề đó như bình thường

bày tỏ

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

Máy chủ

Cuối cùng, bạn phải thêm dòng này vào hoststệp của mình

127.0.0.1 dummy.com

6

Bạn nên xem qua heroku-ssl-redirect . Nó hoạt động như một say mê!

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

4

Nếu bạn đang sử dụng cloudflare.com dưới dạng CDN kết hợp với heroku, bạn có thể bật chuyển hướng ssl tự động trong cloudflare một cách dễ dàng như sau:

  1. Đăng nhập và đi tới trang tổng quan của bạn

  2. Chọn quy tắc trang

    Chọn quy tắc trang

  3. Thêm tên miền của bạn, ví dụ: www.example.com và chuyển luôn sử dụng https thành bật Chuyển luôn sử dụng https thành bật

3

Người dùng vòng lặp có thể sử dụng phiên bản arcseldon answer được điều chỉnh một chút làm phần mềm trung gian:

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

2

Đây là cách Express cụ thể hơn để thực hiện việc này.

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

Với app.use và url động. Hoạt động cả localy và Heroku cho tôi

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

-1

Kiểm tra giao thức trong tiêu đề X-Forwarded-Proto hoạt động tốt trên Heroku, giống như Derek đã chỉ ra. Đối với những gì nó đáng giá, đây là ý chính của phần mềm trung gian Express mà tôi sử dụng và thử nghiệm tương ứng của nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.