良い感じにHTTPS対応したexpress(Node.js)サーバを立てる
目標:
- Lets's Encryptで証明書の作成してHTTPS対応Node.jsサーバを立てる
- QUALYS SSL LABSでA+評価を目指す
環境
まずはOpenSSLのアップデート。話はそれからだ。
OS | CentOS 6.8 |
Node | v7.5.0 |
OpenSSL | openssl-1.0.1e-48.el6_8.3.x86_64 |
厳しい場合は最後のcipher suiteで設定すればなんとかなる。
HTTPSサーバを立てる
まずはHTTPSのサーバを目指す。その後調整してSSLlabsの評価をA+にしていく。
ECDSA対応証明書を作成する
Lets's Encryptの自動作成機能を使って証明書を作成すると署名アルゴリズムがRSA2024になる。今回はせっかくなので楕円曲線DSA(ECDSA)で証明書を作ってみる。
ECDSAとはNSA Suite Bに対応した署名アルゴリズムで、簡単に言うと「強くて早い」。RSAと比べると、クライアント側の処理は少し遅くなるけど、サーバ側の処理はかなり早くなる。
参考:
自堕落な技術者の日記 : RSAとECDSA、署名生成と署名検証どっちが速い? - livedoor Blog(ブログ)
サーバ側のopensslで対応している楕円曲線暗号は以下のコマンドで確認できる
% openssl ecparam -list_curves
この中でSuite Bに対応しているのはprime256v1(P-256)とsecp384r1(P-384)で、もちろんsecp384r1のほうが強い。しかし、prime256v1でもRSAの3072bit並の充分な強さを持つので、負荷を考慮してはprime256v1を使う。
鍵の作成
% openssl ecparam -name prime256v1 -genkey -out server.key
やるだけ。
CSR( Certificate Signing Request )の作成
CSRとは証明書発行の際に必要となる証明書要求ファイル。下図のように、証明書を表示するときにでてくるサブジェクト名欄の部分の情報を入力する。必要なければ空欄でも可。ただし、Common Nameには発行したいドメインを必ず入力すること。
% openssl req -new -sha256 -key server.key -out server.csr
Let's Encryptクライアント & 証明書の発行
以下のコマンドでLet's Encryptクライアントを入手する。
% sudo yum install epel-release % wget https://dl.eff.org/certbot-auto % chmod a+x certbot-auto
CentOS 6 以外は以下のリンクを参照。
Certbot クライアントのインストール - Let's Encrypt 総合ポータル
次にサーバ証明書を発行してもらう。
./certbot-auto certonly --csr ./server.csr
すると、以下のメッセージが表示される。
How would you like to authenticate with the ACME CA? ------------------------------------------------------------------------------- 1: Apache Web Server plugin - Beta (apache) 2: Place files in webroot directory (webroot) 3: Spin up a temporary webserver (standalone) ------------------------------------------------------------------------------- Select the appropriate number [1-3] then [enter] (press 'c' to cancel):
これはサーバの本人証明をするための機能で、
の条件に適した選択肢を選ぶと良い。
(express使っている際は、2の場合にリクエストをさばけない気がするので3が無難か)
カレントディレクトリの中で必要なファイルは以下の3つ。
server.key #(秘密鍵) 0000_cert.pem #(サーバ証明書) 0000_chain.pem #(中間証明書)
expressの設定
express-generatorで雛形を作成後、certディレクトリを作成、先程の3つのファイルを移動させる。binディレクトリ下に以下のようなサーバスクリプトを作成。createServerにoptionsみたいなobjectがあれば大丈夫。
var app = require('../app'); var https = require('https'); var fs = require('fs'); var port = 443; var options = { key: fs.readFileSync('../cert/server.key'), cert: fs.readFileSync('../cert/0000_cert.pem'), ca: fs.readFileSync('../cert/0000_chain.pem') } var server = createServer(options, app); server.listen(port);
なんかいい感じになった。
設定の微調整
SSLlabsでA+にたりない部分を調整していく。
HSTS(HTTP Strict Transport Security)
レスポンスヘッダにHTTPSを強制するオプションを埋め込むことで、クライアントにセキュアな通信を強制させるものがHSTS。ユーザがあるサイトにHTTPプロトコルとして接続するように誘導されると、そのまま平文で通信を始めてSecure属性のないCookieが窃取される恐れがあるが、HSTSはそれを防げたりする。
実装はヘッダーにひとつ要素を加えるだけなので楽チン。app.jsあたりに以下のコードを挿入する。
//add HSTS app.use(function (req, res, next) { res.setHeader( 'Strict-Transport-Security', 'max-age=15552000' ); next(); });
あ、なんかもうA+になってしまったな。。。
感想
おもったよりすぐにA+になってしまった。OCSP Staplingとか診断では指摘されそうな箇所があるけどまぁこんなもんなのかな。。という気持ちになった。
でも、cipher suiteの部分は結構強固になった感じがあったのでよかった。OpenSSLをupdateしたせいか、ほとんどなにも触ってなかったが、以下のようにoptionsオブジェクトを変更するとcipher suiteを設定できたりするみたい。
var options = { key: fs.readFileSync('../cert/server.key'), cert: fs.readFileSync('../cert/0000_cert.pem'), ca: fs.readFileSync('../cert/0000_chain.pem'), ciphers: "HIGH:!LOW:!MEDIUM:!AECDH" }
学びとしてはECDSAの知識が少し増えた。それくらい。。。
今後
現状、サイトのレスポンスがかなり悪いのでちゃんと計測しながら改善していきたいところ。その際はパフォーマンスを考えたセキュリティレベルを設定したいような気がした。