まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:ssl

Node.js + Express の環境で SSL を使う(https でアクセスできるようにする)方法を調べたのでまとめました。


まず SSL を使うための鍵ファイルと証明書ファイルを用意します。公式なドメインを所有していて、本物の鍵/証明書ファイルを持っているのであればそれを使っても構いません。試験的に試すのであれば、いわゆる「オレオレ証明書」を作成します。Linux 環境であれば openssl コマンドを使って、以下のように入力します:
$ openssl genrsa -out server_key.pem 2048
Generating RSA private key, 2048 bit long modulus
...............+++
..........................+++
e is 65537 (0x10001)

$ openssl req -batch -new -key server_key.pem -out server_csr.pem -subj "/C=JP/ST=Chiba/L=Funabashi/O=Jugeme/OU=Dev/CN=juge.me"

$ openssl x509 -in server_csr.pem -out server_crt.pem -req -signkey server_key.pem -days 73000 -sha256
Signature ok
subject=/C=JP/ST=Chiba/L=Funabashi/O=Jugeme/OU=Dev/CN=juge.me
Getting Private key

$

↑この例では juge.me というホスト名で運用する前提での、サーバーの鍵ファイル(server_key.pem)と証明書ファイル(server_crt.pem)を作成しています。

この2つのファイルと同じディレクトリに app.js というファイル名で、Node.js のソースコードを以下の内容で作成しました。アプリケーションそのものはドキュメントルート(/)にアクセスがあった場合に「ハローワールド」と表示するだけの単純なものです:
//. app.js

var express = require( 'express' ),
    http = require( 'http' ),
    https = require( 'https' ),
    cfenv = require( 'cfenv' ),
    fs = require( 'fs' ),
    app = express();
var appEnv = cfenv.getAppEnv();

//. 鍵ファイルと証明書ファイル var options = { key: fs.readFileSync( './server_key.pem' ), cert: fs.readFileSync( './server_crt.pem' ) };
//. 鍵ファイルと証明書ファイルを指定して、https で待受け var server = https.createServer( options, app ).listen( appEnv.port, function(){ console.log( "server stating on " + appEnv.port + " ..." ); });
//. ドキュメントルートにリクエストがあった場合の処理 app.get( '/', function( req, res ){ res.write( 'ハローワールド' ); res.end(); });

このコードを実行すると待受ポート番号が動的に決定して表示されます(↓の例だと 6015 番ポートで https が待ち受けていることが分かります):
$ node app.js
server stating on 6015 ...

ではウェブブラウザで実際にアクセスしてみます。本当に使っている本物のドメイン/ホスト名であればそのままアクセスできると思いますが、試験的に実行している場合はこのホストに "juge.me" という名前でアクセスできる必要があります。必要に応じて hosts ファイルを編集するなどして、目的のホスト(node app.js を実行したホスト)に "juge.me" というホスト名でアクセスできるような準備をしておいてください。

そしてウェブブラウザで "https://juge.me:(ポート番号)" にアクセスします:
2017041301


オレオレ証明書名物「安全な接続ではない」という警告画面になると思います。ここからの手順はウェブブラウザの種類にもよりますが、FireFox の場合であれば「エラー内容」ボタンをクリックしてから「例外を追加」をクリックして、このサイトの警告を無視するための設定を行います。

そして「セキュリティ例外の追加」ダイアログにて、この URL の「セキュリティ例外を承認」します:
2017041302


すると警告が消えて、プログラムで用意したコードが実行され、「ハローワールド」というメッセージが表示されることが確認できます:
2017041303


IBM Bluemix のような PaaS 環境だと、このあたりも含めてアプリケーションサーバー環境が用意されるので楽ですが、素で Node.js 環境を構築する場合はアプリケーション側でも https 対応を実装する必要があり、意外と面倒ね。

IBM Watson の API を使っていたら、ある日を境にこんなエラーが出るようになりました:
javax.net.ssl.SSLException: java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 64

2016110901


エラーが出ているのは URL を指定して REST API を実行する瞬間の箇所です。GET でも POST でも発生しました。

原因を調べるために Watson API Explorer を使って、同じ API を実行してみたところ・・・今度はエラーなく実行できてしまいました:
2016110902


なんだこれは!? というわけで、エラーメッセージをググって調べてみたところ、こんな StackOverflow にこんなスレッドを見つけました:
Receiving “javax.net.ssl.SSLException: java.lang.ArrayIndexOutOfBoundsException” while connecting to “https:” site


ここに書かれている内容が原因であるとすると、暗号化アルゴリズムである TLS のバージョンの変更が原因らしいとのこと。またいくつかの対処方法があるが、クライアント側だけで対応するには JDK のバージョンを 1.8 にしろ、とのことらしいです。

確かに今回エラーが出るようになったシステムでは JDK 1.6 が使われているので、このエラーが発生する条件は満たしています。また Watson API Explorer 側では JDK 1.8 か、Java 以外のアプリケーションサーバーが使われていると仮定すればエラーなしに成功することになります。なので、今回のような結果になっても説明は付くことになります。
2016110903



原因が分かったところで、問題は対処方法です。今回はとあるシステム内からこの API を実行したかったという背景があります。そのシステムに組み込まれている JDK のバージョンが 1.6 である以上、ここを勝手に変更するわけにはいきません。 一方で、クライアントが JDK 1.8 未満である以上はこのエラーを回避するにはサーバー側を変更する必要があります。API 提供されているサーバー設定を変更してもらえる保証もありません。そもそもセキュリティ強化のためのサーバー設定変更が背景にあるわけですが、仮にセキュリティに一旦目をつぶるとして、JDK 1.6 のままでどうにかする方法はないでしょうか?


その方法の1つとして考えられるのが、下図のように JDK 1.8(または他のプラットフォームでもよい)など Watson API を実行できる環境でプロクシー的なものを作って、JDK 1.8 未満のシステムからはこのプロクシーを経由してリクエストを実行し、レスポンスを得る、という仕組みです:
2016110904

こうすると JDK 1.8 未満のシステムからはプロクシーまでアクセスできればよく、またプロクシー側の設定等にも自分である程度手を加えることも可能になります。そして Watson からは JDK 1.8 からのリクエストとして API を実行して結果を返す、という仕組みを作ればうまくいくのではないか、と考えました。まあ2つの仕組みを作ってメンテナンスしなければならない、という点はありますが、技術的にはクリアできる目処が立ちそうです。

近いうちに実際に作ってみた仕組みをまた紹介する予定です。


FireFox を使っていて https のサイトを訪れた時に「安全な接続ができませんでした」というエラーページに出くわすことがあります。それも選択肢が「再試行」と「報告」しかなくて、例外追加みたいな対応もできないケースです:
2015072501


よく見ると SSL が安全ではない、みたいなメッセージも書かれてます。この件に関する Mozzila としての見解はこちらです。昨年ですが、通称「プードルアタック」と呼ばれる脆弱性が SSL v3 に見つかり、SSL v3 は安全ではなくなってしまいました。その対策としてデフォルトでこのプロトコルを使ったサイトをブロックします、とのことです:
https://support.mozilla.org/ja/kb/enable-ssl-fix-cannot-connect-securely-error


まあ何もしないのはまずいと思うし、Mozzila の言いぶんはわかる。利用者には罪はなくて、サイトを提供する側に(安全ではない)SSL v3 を使わないでほしい、ということだと思っています。

とは言っても、すぐに対応できるとは限らないケースもあります。例えばサーバーイメージが仮想的に提供されているような場合は、その仮想イメージで SSL v3 を使っていることもあります。仮想イメージをすぐに更新してほしいと言われても困るし、仮に仮想イメージそのものは更新できたとしても既に仮想環境内で使われているイメージをどうやって更新するか、という問題だってあります。要はサイト提供側だけにこの問題の責任を追わせるのには無理があるのかな・・・と。

では諦めるしかないのか、というとそんなこともありません。ブラウザで利用するユーザーの自己責任にはなりますが、このような SSL v3 のサイトにアクセスするように FireFox の設定を変更することもできます。以下にその方法を紹介しますが、SSLv3 が安全ではないことに変わりはないので、社内の環境など、本当に安全が保証されているサイトでのみ、また目的のサイトへのアクセスが終わったらすぐに設定を元に戻るなど、自己責任で使ってください。

ではその設定方法を紹介します。まず FireFox を起動して、アドレス欄に about:config と入力します。「動作保証外である」という警告メッセージが表示されますが、「細心の注意を払って使用する」ボタンをクリックします:
2015072502


すると FireFox の挙動に関わる各種設定項目の一覧が表示されます。かなり大量にあるので、ここから目的の項目を探すのは大変なので、検索をします:
2015072503


「検索」と書かれたフィールドに "security.ssl3.dhe" と入力すると、少しずつ項目が絞られていきながら、ここまで入力した時点で2つの項目だけが表示されているはずです。またどちらも初期設定値の true が設定されているはずです:
2015072504


これら2行の項目の設定値を false に変更します。変更するには各行をダブルクリックすることで変更できます:
2015072505


この状態で先程エラーに成ったページに再度アクセスすると、ちゃんと見れるようになるはずです。



繰り返しになりますが、この設定状態は必ずしも安全ではないため、目的のページを見終わった後は、2箇所とも設定を元通りに戻しておくことを強くおすすめします:
2015072506


ちなみにプードルアタックとその対策についてはこちらで詳しく書かれていたので、是非参照してください:
SSLv3 の脆弱性 POODLE への対策を行う


以前に IBM Bluemix を独自ドメインで利用する方法について紹介しました:
IBM Bluemix を独自ドメインで運用する


本エントリでは、特に SSL を使って IBM Bluemix を独自ドメイン運用するための注意点と手順を紹介します。


まず独自ドメインを使わない場合、IBM Bluemix 上のウェブアプリケーションには米国リージョンの場合で****.mybluemix.net(英国リージョンであれば ****.eu-gb.mybluemix.net)というホスト名が割り当てられます。**** 部分はアプリケーション作成時に指定したものが使われます。 以下、説明をシンプルにするため、米国リージョンでの利用を前提として説明を続けます)。

このホスト名を SSL で利用する場合の SSL 証明書は mybluemix.net 側で(つまり IBM 側で)用意されていることになります。特に **** 部分にどのような文字列が来るか想定できない前提で SSL を使うので、その証明書はワイルドカード対応のものが用意されている、ということになります。


さて、独自ドメインで IBM Bluemix を使う場合の注意点として、そのドメインの SSL 証明書は利用者が用意する必要があり、加えて上記の理由からその証明書はワイルドカード対応のものを用意する必要がある、ということになります。

現在、SSL 証明書を発行してくれるサービスは国内外でいくつかあります。ただ IBM Bluemix で利用するドメインの SSL 証明書を発行してもらうためには、ワイルドカード対応のものを発行してもらう必要があるのですが、全ての業者がワイルドカード対応の証明書を発行してくれるわけではありませんワイルドカード対応の SSL 証明書を発行してくれる業者の中から選んで注文する、という点に注意が必要です。


ただ、特定の限られた用途での利用など、いわゆる「オレオレ証明書」でもいい、という場合は、自分でワイルドカードに対応した SSL 証明書を用意することでも対応は可能です。ワイルドカードに対応した SSL 証明書を自分で発行する場合の手順はこちらを参照してください:
ワイルドなオレの証明

以下はこの方法で用意した SSL 鍵および証明書を使った前提で紹介を続けます(手順そのものは正式な証明書を使った場合も同様です)。

まず冒頭で紹介した「IBM Bluemix を独自ドメインで運用する」の手順を進めて、「組織の管理」メニューからドメインの追加を行います:
2015030601


追加する独自ドメイン(この例では "yellomix.net")を指定して「保存」します:
2015030602


保存後、「SSL 証明書」と書かれた列に証明書アップロードのためのボタンが表示されるので、ここをクリックします:
2015030603


証明書ファイルと秘密鍵ファイルを指定してアップロードします。もし中間証明書ファイルをお持ちで、追加したい場合は中間証明書ファイルも指定します。最後に「アップロード」ボタンをクリックします:
2015030604


先ほどのボタンがグリーンに変わっていれば証明書ファイルのアップロードは完了です:
2015030605


では独自ドメインでアプリケーション・サーバーを1つ作成して、SSL でアクセスする、という動作確認を行ってみます。まずは Bluemix 上に(動作確認なので中身はあってもなくてもいいと思いますが)ランタイムまたはボイラープレートから作成したランタイムを作ります。その際にドメイン情報として、追加した独自ドメインを指定するようにします:
2015030606


ランタイムアプリケーションを作成すると、経路の情報としてホスト名が表示されます。先ほど独自ドメインを指定したので独自ドメインにアプリ名が付与されたホスト名になっているはずです:
2015030607


DNS 側でもこのホスト名の名前解決ができるような設定をしておきます。このランタイムに付けた名前(dotnsf-ibm-wordpress)の CNAME として、元々デフォルトで割り振られるはずだった名前(dotnsf-ibm-wordpress.mybluemix.net)を参照するように指定します。この部分は各々でお使いの DNS ツールの方法に従ってください:
2015030608


これで設定は完了しています。確認のため実際にブラウザを使って、作成したランタイムのホスト名に対して https:// でアクセスしてみましょう。オレオレ証明書を使った場合は、ブラウザの種類にもよりますが、「接続の安全性を確認できない」云々、といった確認画面になると思います。ある意味、正しく SSL が動いている証拠と言えます:
2015030609


理解した上で許可すれば SSL で(httpsで)見れるようになります。このホスト名を直接指定した形では SSL 証明書を作っていませんでしたが、ワイルドカード指定で作成した SSL 証明書が IBM Bluemix に正しくインポートされて動いていることが確認できました:
2015030610





















 

SSL のオレオレ自己署名証明書を、ワイルドカード対応で作成する手順です。CentOS 上での作業を前提で説明します。
ワイルドカード対応といっても、普通のオレオレ証明書(?)の時と比べてそんなに大きく変わるわけではありません。

CentOS のターミナルを開いて、以下を順次実行していきます。
なお、今回は "yellowmix.net" というドメイン向けのワイルドカード対応証明書を作る前提で説明します。

まずは普通に秘密鍵を作成:
# openssl genrsa -out server.key 2048

次に CSR (署名要求)ファイルを作成、ここでホスト名をワイルドカードで指定します:
# openssl req -new -key server.key -out server.csr
  :
  :
-----
Country Name (2 letter code) [XX]:JP
State or Province Name (full name) []:Chiba
Locality Name (eg, city) [Default City]:Funabashi
Organization Name (eg, company) [Default Company Ltd]:YellowMix Ltd
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:*.yellowmix.net
Email Address []:

  :
  : (残りは全て Enter キーで)

最後に証明書を作成します。この例では約10年間(3653日)有効な証明書を作成しています:
# openssl x509 -days 3653 -in server.csr -out server.crt -req -signkey server.key

ここまでの作業で server.key, server.csr, server.crt の3ファイルができているはずです。

SSL を使ったウェブページの動作確認程度であればこれで充分です。
ワイルドカードで作ったので、ホスト名(****.yellowmix.net の **** 部分)が何であってもこの証明書1つで使えるはず。


次回は、作成したこのワイルドカード対応 SSL 証明書を IBM Bluemix 環境で使ってみます。


(2015/Feb/08 追記 続きはこちら)
IBM Bluemix を独自ドメインで使う(SSL編)

 

このページのトップヘ