web-dev-qa-db-ja.com

node.js / expressを使用した自動HTTPS接続/リダイレクト

作業中の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)がノードの前にあると想定されていますか?

これはウェブ開発への私の最初の進出です。もしこれが明白なものであるならば、私の無知を許してください。

145
Jake

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

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

すべて一緒に、これは私が望んでいた通りに動作します。

149
Jake

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

105
basarat

この男に感謝: 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);
        }
});
57
Jeremias Binder

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

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

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

単一のポートで両方のプロトコルを絶対に処理できるようにする必要がある場合は、nginx、lighttpd、Apache、またはその他のWebサーバーをそのポートに配置し、Nodeのリバースプロキシとして機能する必要があります。

12
Ryan Olds

この回答は、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);
8
NoBrainer

私は、エクスプレスを使用しているときに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();
  } 
});
6
Catalyst

アプリが信頼できるプロキシ(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ヘッダーをチェックできるようにします
5
Jamie McCrindle

ここでのほとんどの回答は、req.headers.Hostヘッダーを使用することを示唆しています。

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

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

常にすべての入力を検証する。これは妄想ではありません。サービスにHostヘッダーのないリクエストを受け取りました。

また、RLを文字列として扱わないでください。ノードURLモジュールを使用して、文字列の特定の部分を変更します。 URLを文字列として扱うことは、多くのさまざまな方法で活用できます。しないでください。

4
arboreal84

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);
4
var express = require('express');
var app = express();

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

app.listen(80);

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

3
Nick Kotenberg

「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)
3
luofei614

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

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

また、ルート権限なしでnodejsを起動するために、標準ではないポートを使用することを好みます。私は8080と8443が好きです。なぜなら、Tomcatでの長年のプログラミングから来ているからです。

私の完全なファイルは

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

次に、HTTPおよびHTTPSポートで80および443トラフィックを予測するためにiptableを使用します。

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
3
Lorenzo Eccher

これは私のために働いた:

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

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

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

私がやった方法は次のとおりです(restify.jsを使用しますが、express.jsまたはノード自体でも動作するはずです)

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

1
awaage

これは私のために働いた:

/* 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を使用します