2011-12-22 27 views
44

我正在嘗試創建Express.js的中間件以將所有非安全(端口80)通信重定向到安全SSL端口(443)。 不幸的是,Express.js請求中沒有任何信息可以讓您確定請求是通過http還是https傳出的。如何在Express.js中強制使用SSL/https

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

注:

  1. 有沒有可能與Apache或別的東西來處理它。它要在節點中完成。

  2. 只有一個服務器可以在應用程序中啓動。

你會如何解決這個問題?

+1

可能重複的http://堆棧溢出。com/questions/7450940/automatic-https-connection-redirect-with-node-js-express – clyfe 2011-12-22 15:07:47

+0

此問題的解決方案是重定向每個請求。但就像我寫的那樣,這是沒有選擇的。 – Elias 2011-12-22 15:09:24

+0

它只重定向http的請求。 – clyfe 2011-12-22 15:13:57

回答

0

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

編輯:更新了網址

+0

這個頁面不見了,所以我是downvoting。如果它回來,請讓我知道,所以我可以投票回來!這可以通過複製這裏的一些內容來避免。謝謝。 – 2012-02-05 03:43:51

+0

感謝您的信息,我更新了網址 – Elias 2012-03-14 11:51:28

+0

非常好!改變了我的投票。 (回想起來我也許應該只是留下了評論,而不是downvoted它。) – 2012-03-14 20:02:01

21

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

考慮到這個約束,你的問題似乎是,由於你沒有提供的原因,你的客戶端連接到錯誤的端口。這是一個奇怪的邊緣情況,因爲默認情況下,客戶端將向端口80發送HTTP請求,向端口443發送HTTPS。當我默認「默認」時,我的意思是如果沒有特定的端口包含在URL中。因此,除非您明確使用諸如http://example.com:443https://example.com:80之類的交叉網址,否則您確實不應該在您的網站上發生任何縱橫交錯的流量。但是既然你問了這個問題,我想你一定有它,但我敢打賭你使用的是非標準端口而不是80/443的默認值。

所以,對於背景:是的,一些網絡服務器處理這個相當好。例如,如果您對nginx執行http://example.com:443,它將使用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 --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 
+2

安全問題:假設您的應用程序使用cookie身份驗證,並且登錄的人導航到網站的http:// ...部分,攻擊者是否有可能在重定向發生之前嗅探其Cookie? – Costa 2012-06-30 21:26:27

+8

是的,我相信這是可能的,這就是爲什麼你應該在你的cookie頭中設置「安全」標誌,這將指示瀏覽器不要通過HTTP傳輸cookie,只有HTTPS。 http://en.wikipedia.org/wiki/HTTP_cookie#Secure_and_HttpOnly – 2012-06-30 22:32:49

+0

好的,太棒了!只需要弄清楚如何讓CouchDB設置一個安全標誌。謝謝!! – Costa 2012-07-01 13:04:14

3

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

https.createServer(選項,app.handle)。聽(443)

特快2X

2

此代碼看起來像它你需要:https://gist.github.com/903596

+0

我試過了,它不起作用。我得到:錯誤:發送後無法設置標題。之後節點進程就會死亡。 – 2012-06-05 14:52:18

23

雖然問題看起來歲,我想回答,因爲它可能幫助別人。它最新版本的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

Store中的證書和密鑰文件中包含的文件夾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 
}; 

現在通過options對象在createServer()函數

express.createServer(options); 

完成!

+1

如果明確告訴你不要叫'createServer' - http://stackoverflow.com/questions/11804202/how-to-setup-a-ssl-cert-in-express-js-server-now – 2013-12-09 13:02:52

+1

鏈接不再作品 – ezraspectre 2014-02-25 14:04:54

10

基於埃利亞斯的答案,但與內聯代碼。如果您有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); 
    } 
}); 
+1

這樣做對中間人的攻擊非常脆弱。見HSTS:http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security – Qualcuno 2014-03-04 05:11:13

+1

從@喬納森-TRAN的答案是正確的,它不是任何更容易受到男人比不這樣做中間人攻擊。對於那些你理想地希望與這樣的事情結合使用的客戶端來說,HSTS是一種額外的保護措施(儘管它在IE 11以後仍然不被IE支持)。 – 2015-05-14 13:54:28

+0

我得到這個錯誤從鉻:ERR_TOO_MANY_REDIRECTS – 000000000000000000000 2015-09-28 11:25:47

16

爲了防止您在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(); 
} 

你可以用快遞(2.x和4.x版)像這樣使用它:

app.use(requireHTTPS); 
2

試試這個例子:

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