node.js / expressによる自動HTTPS接続/リダイレクト


181

作業中のnode.jsプロジェクトでHTTPSをセットアップしようとしています。この例では、基本的にnode.jsのドキュメントに従っています

// curl -k https://localhost:8000/
var https = require('https');
var fs = require('fs');

var options = {
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world\n");
}).listen(8000);

今、私がするとき

curl -k https://localhost:8000/

私は得る

hello world

予想通り。しかし、私がするなら

curl -k http://localhost:8000/

私は得る

curl: (52) Empty reply from server

振り返ってみると、これはこのように機能することは明らかですが、同時に、最終的に私のプロジェクトにアクセスする人はhttps:// yadayadaと入力しないため、ヒットした瞬間からすべてのトラフィックをhttpsにする必要がありますサイト。

ノード(および私が使用しているフレームワークであるExpress)が、指定されているかどうかに関係なく、すべての着信トラフィックをhttpsにハンドオフする方法を教えてください。これに対処したドキュメントは見つかりませんでした。それとも、実稼働環境では、ノードの前に何かがあり(nginxなど)、この種のリダイレクトを処理するものと想定されていますか?

これはWeb開発への私の最初の進出です。これが明らかなものである場合、私の無知を許してください。


ここで簡潔にこれに答えました:stackoverflow.com/a/23894573/1882064
arcseldon

3
HEROKUにデプロイする人にとっては、この質問の回答は役に立ちません(「リダイレクトが多すぎます」)が、別の質問のこの回答は役に立ちます
Rico Kahler

回答:


179

ライアン、正しい方向に向けてくれてありがとう。私はあなたの答え(2番目の段落)をいくつかのコードで少し具体化し、それが機能します。このシナリオでは、これらのコードスニペットがExpressアプリに配置されています。

// set up plain http server
var http = express.createServer();

// set up a route to redirect http to https
http.get('*', function(req, res) {  
    res.redirect('https://' + req.headers.host + req.url);

    // Or, if you don't want to automatically detect the domain name from the request header, you can hard code it:
    // res.redirect('https://example.com' + req.url);
})

// have it listen on 8080
http.listen(8080);

httpsエクスプレスサーバーは3000でATMをリッスンします。ノードがルートとして実行される必要がないように、これらのiptablesルールを設定しました。

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 3000

すべて一緒に、これは私が望んだとおりに機能します。


15
本当に重要な質問(セキュリティに関して)。実際にリダイレクトが発生する前に、攻撃者がCookie(セッションID)を傍受して盗むことは可能ですか?
コスタ

4
この結果の症状をどのように修正しますか?Error 310 (net::ERR_TOO_MANY_REDIRECTS): There were too many redirects
bodine 2013年

16
実際、これはより良いようです ...リダイレクトをラップするだけif(!req.secure){}
bodine

6
-私は別のポストに与えられたセキュリティに関するコスタの重要な質問を@に答えを指摘したいstackoverflow.com/questions/8605720/...
ThisClark

2
if(req.protocol==='http')ステートメントをまとめたい場合があります
Max

120

HTTPがデフォルトでポート80を試行し、HTTPSがデフォルトでポート443を試行するため、従来のポートを使用する場合、2台のサーバーを同じマシン上に置くことができます。コードは次のとおりです。

var https = require('https');

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

https.createServer(options, function (req, res) {
    res.end('secure!');
}).listen(443);

// Redirect from http port 80 to https
var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url });
    res.end();
}).listen(80);

httpsでテストする:

$ curl https://127.0.0.1 -k
secure!

http:

$ curl http://127.0.0.1 -i
HTTP/1.1 301 Moved Permanently
Location: https://127.0.0.1/
Date: Sun, 01 Jun 2014 06:15:16 GMT
Connection: keep-alive
Transfer-Encoding: chunked

詳細:Nodejs HTTPとHTTPSを同じポートで


4
res.writeHead(301, etc.)301クライアントに同じメソッドを使用するように指示していないため、GET呼び出しに対してのみ正しく機能します。使用したメソッド(および他のすべてのパラメーター)を保持したい場合は、を使用する必要がありますres.writeHead(307, etc.)。それでも機能しない場合は、プロキシを実行する必要があります。出典:http
//stackoverflow.com/a/17612942/1876359

113

この男のおかげで:https : //www.tonyerwin.com/2014/09/redirecting-http-to-https-with-nodejs.html

app.use (function (req, res, next) {
        if (req.secure) {
                // request was via https, so do no special handling
                next();
        } else {
                // request was via http, so redirect to https
                res.redirect('https://' + req.headers.host + req.url);
        }
});

11
これが一番の答えです!
Steffan

3
同意し、最初の答えにする必要があります。
1984年

12
次の行が私にとってこのソリューションの作業を助けました:app.enable('trust proxy');
arbel03 '10

2
上記のように^^^^:トラフィックをリダイレクトするあらゆる種類のプロキシ、ファイアウォール、またはロードバランサーの背後にある場合は、「信頼プロキシ」が必要です。たとえば、すべてのhttpおよびhttpsリクエストを同じ非ルートポートに送信するAWSロードバランシング(例:3000)VPCのウェブサーバー。
18

1
AWS ELB用のapp.get('X-Forwarded-Proto') != 'http'代わりのreq.secure aws.amazon.com/premiumsupport/knowledge-center/...
SHAK

25

Nginxを使用すると、「x-forwarded-proto」ヘッダーを利用できます。

function ensureSec(req, res, next){
    if (req.headers["x-forwarded-proto"] === "https"){
       return next();
    }
    res.redirect("https://" + req.headers.host + req.url);  
}

5
req.headers ["x-forwarded-proto"] === "https")は信頼できないことがわかりましたが、req.secureは機能します!
Zugwalt 2013年

私は同じことを見つけました、これは非常に信頼できないようです。
dacopenhagen 2013年

2
req.secureは、プロキシの背後にある場合req.secureはあなたのプロトがあると言うことがプロキシ急行の後ろに、プロト==「HTTPS」に相当するので、しかし、これはバグがあり、適切な方法でHTTPS、HTTP
dacopenhagen

7
app.enable('trust proxy');「アプリが前面プロキシの背後にあり、X-Forwarded- *ヘッダーを使用してクライアントの接続とIPアドレスを判別する必要があることを示します。」expressjs.com/en/4x/api.html#app.set
dskrvk

URLを文字列として扱わないで、代わりにurlモジュールを使用してください。
arboreal84 2016

12

0.4.12の時点では、NodeのHTTP / HTTPSサーバーを使用して同じポートでHTTPおよびHTTPSをリッスンする実際のクリーンな方法はありません。

一部の人々は、NodeのHTTPSサーバー(これはExpress.jsでも機能します)に443(または他のポート)をリッスンさせ、小さなhttpサーバーを80にバインドしてユーザーを安全なポートにリダイレクトさせることでこの問題を解決しました。

1つのポートで両方のプロトコルを処理する必要がある場合は、nginx、lighttpd、apache、またはその他のWebサーバーをそのポートに配置し、ノードのリバースプロキシとして機能させる必要があります。


ライアンに感謝します。「... 80に小さなhttpサーバーをバインドしてリダイレクトする...」とは、別のノードの httpサーバーを意味していると思います。私はこれがどのように機能するかについての考えを持っていると思いますが、任意のサンプルコードを指摘できますか?また、この問題の「よりクリーンな」ソリューションがノードロードマップのどこかにあるかどうかを知っていますか?
ジェイク、

Node.jsアプリケーションには、複数のhttp(s)サーバーを含めることができます。Node.jsの問題(github.com/joyent/node/issues)とメーリングリスト(groups.google.com/group/nodejs)を確認しましたが、報告された問題はありませんでしたが、問題に関するいくつかの投稿はありましたメーリングリストに。私の知る限り、これはパイプの中にありません。私はそれをgithubで報告し、どのようなフィードバックが得られるかを確認することをお勧めします。
ライアンオールズ

本当に重要な質問(セキュリティに関して)。そのリダイレクトが実際に発生する前に、「攻撃者」がCookie(セッションID)を傍受して盗むことは可能ですか?
コスタ

はい、3xxステータスコードと、場合によってはLocationヘッダーがエージェントに送信されます。この時点で、エージェントはLocationヘッダーで指定されたURLを要求します。
ライアンオールズ

それは2015年ですが、まだそうですか?
TheEnvironmentalist 2015年

10

express-force-httpsモジュールを使用できます。

npm install --save express-force-https

var express = require('express');
var secure = require('express-force-https');

var app = express();
app.use(secure);

1
パッケージは何年も更新されていないため、注意して使用してください。エンコーディングが悪いAWSで問題が発生しました。
Martavis P.

1
姉妹パッケージ:npmjs.com/package/express-to-https -しかし、それは/ isbetterの/ etc動作するか私はわかりません
ケツァルコアトル

ミドルウェアを使用して静的ファイル(単一ページのアプリを含む)を提供する場合は、リダイレクトミドルウェアをapp最初に接続する必要があります。(この回答を参照)
マシューR.

9

私はバサラトが提案したソリューションを使用していますが、HTTPおよびHTTPSプロトコル用に2つの異なるポートを使用していたため、ポートを上書きする必要もあります。

res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url });

root権限なしでnodejsを起動するために、標準ポートを使用しないことも好みます。Tomcatで何年にもわたってプログラミングをしてきたので、8080と8443が好きです。

私の完全なファイルは

var fs = require('fs');
var http = require('http');
var http_port    =   process.env.PORT || 8080; 
var app = require('express')();

// HTTPS definitions
var https = require('https');
var https_port    =   process.env.PORT_HTTPS || 8443; 
var options = {
   key  : fs.readFileSync('server.key'),
   cert : fs.readFileSync('server.crt')
};

app.get('/', function (req, res) {
   res.send('Hello World!');
});

https.createServer(options, app).listen(https_port, function () {
   console.log('Magic happens on port ' + https_port); 
});

// Redirect from http port to https
http.createServer(function (req, res) {
    res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url });
    console.log("http request, will go to >> ");
    console.log("https://" + req.headers['host'].replace(http_port,https_port) + req.url );
    res.end();
}).listen(http_port);

次に、iptableを使用して、HTTPおよびHTTPSポートで80および443トラフィックを前向きにします。

sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8443

ロレンゾに感謝します。これは私にとってうまくいきました。オプションを使用する代わりに、letsencryptを使用しました。varオプションを削除しました。letsencryptパラメータ(privateKey、certificate、ca、および信任状)を追加し、オプションを信任状に変更しました:https.createServer(credentials、app)
Ha

8

この回答は、Express 4.0で動作するように更新する必要があります。これは、個別のhttpサーバーを機能させる方法です。

var express = require('express');
var http = require('http');
var https = require('https');

// Primary https app
var app = express()
var port = process.env.PORT || 3000;
app.set('env', 'development');
app.set('port', port);
var router = express.Router();
app.use('/', router);
// ... other routes here
var certOpts = {
    key: '/path/to/key.pem',
    cert: '/path/to/cert.pem'
};
var server = https.createServer(certOpts, app);
server.listen(port, function(){
    console.log('Express server listening to port '+port);
});


// Secondary http app
var httpApp = express();
var httpRouter = express.Router();
httpApp.use('*', httpRouter);
httpRouter.get('*', function(req, res){
    var host = req.get('Host');
    // replace the port in the host
    host = host.replace(/:\d+$/, ":"+app.get('port'));
    // determine the redirect destination
    var destination = ['https://', host, req.url].join('');
    return res.redirect(destination);
});
var httpServer = http.createServer(httpApp);
httpServer.listen(8080);

1
httpApp.use( '*'、httpRouter);を変更することでこれを機能させました。httpApp.use( '/'、httpRouter);に hhtpサーバーを作成する前に、それを行に移動します。
KungWaz 2014年

8

アプリが信頼できるプロキシ(AWS ELBまたは正しく構成されたnginxなど)の背後にある場合、このコードは機能するはずです。

app.enable('trust proxy');
app.use(function(req, res, next) {
    if (req.secure){
        return next();
    }
    res.redirect("https://" + req.headers.host + req.url);
});

ノート:

  • これは、80と443でサイトをホストしていることを前提としています。そうでない場合は、リダイレクトするときにポートを変更する必要があります。
  • これは、プロキシでSSLを終了していることも前提としています。SSLをエンドツーエンドで実行している場合は、上記の@basaratからの回答を使用してください。エンドツーエンドSSLがより良いソリューションです。
  • app.enable( 'trust proxy')は、エクスプレスがX-Forwarded-Protoヘッダーを確認できるようにします

6

Expressを使用しているときにreq.protocolが機能することがわかりました(テストせずにテストしましたが、機能すると思われます)。Express 3.4.3で現在のノード0.10.22を使用

app.use(function(req,res,next) {
  if (!/https/.test(req.protocol)){
     res.redirect("https://" + req.headers.host + req.url);
  } else {
     return next();
  } 
});

5

ここでのほとんどの回答は、req.headers.hostヘッダーの使用を提案しています。

HostヘッダーはHTTP 1.1で必要ですが、ヘッダーは実際にはHTTPクライアントによって送信されない可能性があるため、実際にはオプションであり、node / expressはこの要求を受け入れます。

あなたは尋ねるかもしれません:どのHTTPクライアント(例:ブラウザー)はそのヘッダーのないリクエストを送信できますか?HTTPプロトコルは非常に簡単です。HTTPリクエストを数行のコードで作成して、ホストヘッダーを送信しないようにすることができます。不正なリクエストを受信するたびに例外をスローし、そのような例外の処理方法によっては、サーバーがダウンする可能性があります。

したがって、常にすべての入力を検証してください。これはパラノイアではなく、サービスにホストヘッダーがないリクエストを受け取りました。

また、URLを文字列として扱わないでください。node urlモジュールを使用して、文字列の特定の部分を変更します。URLを文字列として扱うことは、多くの方法で悪用される可能性があります。しないでください。


あなたが例を挙げていれば、あなたの答えは完璧でしょう。
SerG 2018年

正当に聞こえますが、いくつかの参照/リンクは素晴らしいでしょう。
Giwan

3
var express = require('express');
var app = express();

app.get('*',function (req, res) {
    res.redirect('https://<domain>' + req.url);
});

app.listen(80);

これは私たちが使用するものであり、うまく機能します!


1
URLを文字列として扱わないで、代わりにurlモジュールを使用してください。
arboreal84 2016

4
責任感のある例を挙げてください。スタックオーバーフローは電話ゲームです。
arboreal84 2016

1
これは無限ループを引き起こしませんか?
Muhammad Umer 2017年

いいえ、それはポート80でのみリッスンし、ポート443に送信するためです。無限ループが発生する唯一の方法は、443の一部のリスナーが私のコードにない80にリダイレクトする場合です。私はそれが理にかなっていると思います。ありがとう!
Nick Kotenberg 2017

3

「net」モジュールを使用して、同じポートでHTTPおよびHTTPSをリッスンできます

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

var net=require('net');
var handle=net.createServer().listen(8000)

var options = {
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world\n");
}).listen(handle);

http.createServer(function(req,res){
  res.writeHead(200);
  res.end("hello world\n");
}).listen(handle)

5
Node 0.8でこれを実行すると、.listenを呼び出す最後のサーバーだけが応答するようです。この場合、HTTPは機能しますが、HTTPSは機能しません。.createServerの順序を逆にすると、HTTPは機能しますがHTTPSは機能しません。:(
Joe

1
これは、説明どおりには機能しません。ジョーが見た問題を確認できます。
ステパンマズロフ2013年

2

これは私のために働きました:

app.use(function(req,res,next) {
    if(req.headers["x-forwarded-proto"] == "http") {
        res.redirect("https://[your url goes here]" + req.url, next);
    } else {
        return next();
    } 
});

これは正しいかもしれませんが、これが質問者の質問にどのように答えるかについて詳しく説明する必要があります。
Joe C

1

2つのNode.jsサーバーをインスタンス化できます-1つはHTTPおよびHTTPS用です

両方のサーバーが実行するセットアップ関数を定義することもできるので、多くの重複したコードを記述する必要はありません。

これが私がやった方法です:(restify.jsを使用しますが、express.jsまたはノード自体でも機能するはずです)

http://qugstart.com/blog/node-js/node-js-restify-server-with-both-http-and-https/


0

これは私のために働きました:

/* Headers */
require('./security/Headers/HeadersOptions').Headers(app);

/* Server */
const ssl = {
    key: fs.readFileSync('security/ssl/cert.key'),
    cert: fs.readFileSync('security/ssl/cert.pem')
};
//https server
https.createServer(ssl, app).listen(443, '192.168.1.2' && 443, '127.0.0.1');
//http server
app.listen(80, '192.168.1.2' && 80, '127.0.0.1');
app.use(function(req, res, next) {
    if(req.secure){
        next();
    }else{
        res.redirect('https://' + req.headers.host + req.url);
    }
});

httpsにリダイレクトする前にヘッダーを追加することをお勧めします

今、あなたがするとき:

curl http://127.0.0.1 --include

あなたが得る:

HTTP/1.1 302 Found
//
Location: https://127.0.0.1/
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 40
Date: Thu, 04 Jul 2019 09:57:34 GMT
Connection: keep-alive

Found. Redirecting to https://127.0.0.1/

Express 4.17.1を使用しています

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.