web-dev-qa-db-ja.com

Express.jsでSSL / httpsを強制する方法

Express.js用のミドルウェアを作成して、すべての非セキュア(ポート80)トラフィックをセキュアSSLポート(443)にリダイレクトしようとしています。残念ながら、Express.jsリクエストには、リクエストがhttp経由かhttps経由かを判断できる情報はありません。

1つの解決策はeveryリクエストをリダイレクトすることですが、これは私にとって選択肢ではありません。

ノート:

  1. Apacheなどで処理する可能性はありません。 hasノードで行われます。

  2. oneサーバーのみがアプリケーションで起動できます。

どのように解決しますか?

55
Elias

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

質問は1年前に見えますが、他の人に役立つかもしれないので答えたいと思います。 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);

できた!

26
MeetM

まず、問題を明確にできるかどうかを確認します。 1つのnode.jsプロセスに制限されていますが、そのプロセスは2つのネットワークポート(80と443の両方)でリッスンできますか? (oneサーバーと言うとき、1つのプロセスを意味するのか、1つのネットワークポートのみを意味するのかは明確ではありません)。

その制約を考えると、問題は、あなたが提供しない理由のために、どういうわけかあなたのクライアントが間違ったポートに接続しているようです。これは奇妙なEdgeの場合です。なぜなら、デフォルトでは、クライアントはHTTP要求をポート80に、HTTPSをポート443に作成するからです。したがって、 http://example.com:44https://example.com:8 のような交差URLを明示的に使用しているのでなければ、実際にはすべきではありません交差するトラフィックがサイトに到達している。しかし、あなたが質問をしたので、あなたはそれを持っているに違いないと思いますが、80/443のデフォルトではなく、非標準のポートを使用しているに違いないと思います。

そのため、背景について:はい、一部のWebサーバーはこれを適切に処理します。たとえば、 http://example.com:44 をnginxに送信すると、「プレーンHTTP要求がHTTPSポートに送信されました」というHTTP 400「Bad Request」応答で応答します。はい、同じnode.jsプロセスから80と443の両方でリッスンできます。 express.createServer()の2つの個別のインスタンスを作成する必要があるだけなので、問題ありません。以下に、両方のプロトコルの処理を示す簡単なプログラムを示します。

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ポートにヒットし、通常のTLSハンドシェイクの代わりにその接続をHTTPサーバーに配線します。

これらのいずれかを実行すると、エクスプレスエラーハンドラは呼び出されません。

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
21
Peter Lyons

エリアスの回答に基づいていますが、インラインコードを使用しています。これは、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);
  }
});
10
Jonathan Tran

http.createServer(app.handle).listen(80)

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

エクスプレス2x

3
TJ Holowaychuk

このコードは、必要なことを行うように見えます: https://Gist.github.com/903596

2
robocat

この例を試してください:

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);
        });
2
Ashok Kumawat