簡體   English   中英

如何在Express.js中強制使用SSL / https

[英]How to force SSL / https in Express.js

我正在嘗試為Express.js創建一個中間件,將所有非安全(端口80)流量重定向到安全的SSL端口(443)。 不幸的是,Express.js請求中沒有信息可以讓您確定請求是通過http還是https。

一種解決方案是重定向每個請求,但這不是我的選擇。

筆記:

  1. 使用Apache或其他方法無法處理它。 必須在節點中完成。

  2. 在應用程序中只能啟動一個服務器。

你會如何解決這個問題?

萬一你在Heroku上托管並且只想重定向到HTTPS而不管端口,這里是我們正在使用的中間件解決方案。

如果您在本地開發,則無需重定向。

function requireHTTPS(req, res, next) {
  // The 'x-forwarded-proto' check is for Heroku
  if (!req.secure && req.get('x-forwarded-proto') !== 'https' && process.env.NODE_ENV !== "development") {
    return res.redirect('https://' + req.get('host') + req.url);
  }
  next();
}

您可以像Express(2.x和4.x)一樣使用它:

app.use(requireHTTPS);

雖然問題看起來已經有一年了,但我想回答一下,因為它可能對其他人有所幫助。 使用最新版本的expressjs(2.x)實際上非常簡單。 首先使用此代碼創建密鑰和證書

openssl genrsa -out ssl-key.pem 1024

$ openssl req -new -key ssl-key.pem -out certrequest.csr ..一堆提示

$ openssl x509 -req -in certrequest.csr -signkey ssl-key.pem -out ssl-cert.pem

將證書和密鑰文件存儲在包含app.js的文件夾中。 然后編輯app.js文件並在express.createServer()之前編寫以下代碼

var https = require('https');
var fs = require('fs');

var sslkey = fs.readFileSync('ssl-key.pem');
var sslcert = fs.readFileSync('ssl-cert.pem')

var options = {
    key: sslkey,
    cert: sslcert
};

現在傳遞createServer()函數中的options對象

express.createServer(options);

完成!

首先,讓我看看我是否可以澄清問題。 您只能使用一(1)個node.js進程,但該進程可以偵聽兩(2)個網絡端口,包括80和443,對嗎? (當你說一台服務器時,你不清楚你是指一個進程還是只有一個網絡端口)。

鑒於這種約束,你問題似乎是,由於你沒有提供的原因,你的客戶端會以某種方式連接到錯誤的端口。 這是一個奇怪的邊緣情況,因為默認情況下,客戶端將向端口80和HTTPS發出HTTP請求到端口443.當我說“默認情況下”時,我的意思是如果URL中沒有包含特定端口。 因此,除非您明確使用像http://example.com:443https://example.com:80這樣的縱橫交錯的網址,否則您的網站不應該有任何縱橫交錯的流量。 但是既然你問了這個問題,我想你必須擁有它,雖然我打賭你使用的是非標准端口而不是80/443默認端口。

因此,對於背景:是的,一些Web服務器處理得相當好。 例如,如果您對http://ginample.com:443執行nginx,它將響應HTTP 400“錯誤請求”響應,指示“普通HTTP請求已發送到HTTPS端口”。 是的,您可以從同一個node.js進程中偵聽80和443。 您只需要創建2個單獨的express.createServer()實例,這樣就沒問題了。 這是一個簡單的程序來演示處理這兩種協議。

var fs = require("fs");
var express = require("express");

var http = express.createServer();

var httpsOptions = {
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem')
};

var https = express.createServer(httpsOptions);

http.all('*', function(req, res) {
  console.log("HTTP: " + req.url);
  return res.redirect("https://" + req.headers["host"] + req.url);
});

http.error(function(error, req, res, next) {
  return console.log("HTTP error " + error + ", " + req.url);
});

https.error(function(error, req, res, next) {
  return console.log("HTTPS error " + error + ", " + req.url);
});

https.all('*', function(req, res) {
  console.log("HTTPS: " + req.url);
  return res.send("Hello, World!");
});

http.listen(80);

我可以通過cURL測試這個:

$ curl --include --silent http://localhost/foo
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Content-Type: text/html
Location: https://localhost/foo
Connection: keep-alive
Transfer-Encoding: chunked

<p>Moved Temporarily. Redirecting to <a href="https://localhost/foo">https://localhost/foo</a></p>

$ curl --include --silent --insecure https://localhost:443/foo
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 13
Connection: keep-alive

Hello, World!%

並顯示從HTTP到HTTPS的重定向...

curl --include --silent --location --insecure 'http://localhost/foo?bar=bux'
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Content-Type: text/html
Location: https://localhost/foo?bar=bux
Connection: keep-alive
Transfer-Encoding: chunked

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 13
Connection: keep-alive

Hello, World!%

因此,這將適用於常規案例的兩個協議並正確地重定向。 但是,十字架根本不起作用。 我相信一個快速交叉的請求命中快速服務器不會通過中間件堆棧路由,因為它從一開始就被視為錯誤,甚至不會正確解析請求URI,這是必要的通過路由中間件鏈發送它。 快速堆棧甚至沒有得到它們我認為因為它們不是有效請求,所以它們在節點TCP堆棧中的某處被忽略。 可能編寫一個服務器來執行此操作,並且可能已經有一個模塊,但您必須直接在TCP層編寫它。 並且您必須在第一塊客戶端數據中檢測到常規HTTP請求,這些客戶端數據訪問TCP端口並將該連接連接到HTTP服務器而不是正常的TLS握手。

當我執行其中任何一個時,我的快速錯誤處理程序不會被調用。

curl  --insecure https://localhost:80/foo
curl: (35) Unknown SSL protocol error in connection to localhost:80

curl http://localhost:443/foo
curl: (52) Empty reply from server

基於Elias的答案,但內聯代碼。 如果nginx或負載均衡器后面有節點,則此方法有效。 Nginx或負載均衡器將始終使用普通的舊http命中節點,但它會設置一個標頭以便您區分。

app.use(function(req, res, next) {
  var schema = req.headers['x-forwarded-proto'];

  if (schema === 'https') {
    // Already https; don't do anything special.
    next();
  }
  else {
    // Redirect to https.
    res.redirect('https://' + req.headers.host + req.url);
  }
});

http.createServer(app.handle)。聽(80)

https.createServer(options,app.handle).listen(443)

快遞2x

試試這個例子:

var express = require('express');
        var app = express();
        // set up a route to redirect http to https
        app.use(function (req, res, next) {
        if (!/https/.test(req.protocol)) {
            res.redirect("https://" + req.headers.host + req.url);
        } else {
            return next();
        }
        });
        var webServer = app.listen(port, function () {
            console.log('Listening on port %d', webServer.address().port);
        });

這段代碼看起來像你需要的: https//gist.github.com/903596

由於我正在使用nginx,我可以訪問標頭的x-forwarded-proto屬性,因此我可以編寫一個小型中間件來重定向所有流量,如下所述: http//elias.kg/post/14971446990/force-ssl-與-EXPRESS-JS-上的Heroku,nginx的

編輯:更新了網址

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM