node-v0.12のtlsを256倍使いこなす方法
DESCRIPTION
new TLS feature in Node-v0.12TRANSCRIPT
Node-v0.12のTLSを256倍使いこなす方法
大津繁樹 (@jovi0608)
株式会社インターネットイニシアティブ(IIJ)
東京Node学園祭2014
2014年11月15日
自己紹介
• 名前:大津繁樹
• 所属: 株式会社インターネットイニシアティブ(IIJ) アプリケーション開発部
• Twitter: @jovi0608
• ブログ:ぼちぼち日記 http://d.hatena.ne.jp/jovi0608/
• GitHub: https://github.com/shigeki/
• 新技術の検証・評価を行ってます。 (Node.js, SPDY, HTTP/2,HTML5)
• iij-http2の開発を通じてIETFのHTTP/2標準化作業に参画中
• Node-v0.11.xへのパッチ提出はわずか。でもほとんどTLS関連です。
TLS (Transport Layer Security)の利用状況
TLS通信認証、暗号化、改ざん防止
図参照 https://plus.google.com/+IlyaGrigorik/posts/7VSuQ66qA3C
GoogleによるChromeの統計調査から、ここ2年間でhttpsサイトへのナビゲーションは28%から58%に増加
IAB(Internet Architecture Board)からインターネットの信頼性に関する声明
https://www.iab.org/2014/11/14/iab-statement-on-internet-confidentiality/
IABは、現在プロトコル設計者・開発者・運用者がインターネットトラフィックの標準として暗号化を行うことが重要であると信じている。できる限り認証と共に暗号を使うべきだが、認証なしの機密性を持つプロトコルでもRFC7258で記載された「広範囲な盗聴行為」に対して有用となりえる。
発表内容
• NodeのTLSモジュール大改造
• TLSSocket
• AES-NI
• PFS (Perfect Forward Secrecy)
• TLS False Start
• TLS Ticket
• OCSP Stapling
• TLS Dynamic Record
• SPDY, HTTP/2
• SPDYのメリットが一番よくわかるデモ
Node-v0.12でTLSを使うために有用な情報をお伝えします。(でも256個もないです)
(注1: Node-v0.12はまだ未リリースですが、2014/11/10時点のv0.12ブランチHEADを対象としています。)
(注2: Node-v0.11.xはまだPOODLE対策がされていません。SSLv3を無効化するために、secureOptions: require(‘constants’).SSL_OP_NO_SSLv3)をサーバオプションに追加しましょう。)
目指せ A+ のTLSサーバ
ソースはこちら
https://gist.github.com/shigeki/986c53242f5bd3d78609
https://www.ssllabs.com/ssltest/index.html
Node TLSモジュールの大改造
Node-v0.10の問題:パフォーマンスが悪い、APIが扱いづらい
•受信した暗号文をnetモジュールで受けてからSecurePairオブジェクトで復号化
•送信する平文もSecurePairオブジェクトで暗号化してからnetモジュールで送信
• C++ → JS → C++ → JSのオーバヘッドが大きい
•平文のCleartextStreamはnet.Socketと似て非なるもの。
Node-v0.10のTLS処理概要
C++
JS
C++
JS
SecurePair
openssl
暗号文
平文 平文
暗号文
暗号文
net module
libuv
CleartextStream
CryptoStream
Node TLSモジュールの大改造
C++C++
JS
tls_wrap
libuv openssl
Node_BIO
TLSSocket
平文 平文
暗号文
Node-v0.12のTLS処理概要
Node-v0.12でパフォーマンスを改善、APIを統一
• opensslとlibuvでデータと共有する Node_BIOを作成 → 両者の処理を一体化
• netモジュールのハンドルをtls_wrapが横取り → C++の処理だけで平文データを生成
• net.Socketを継承するTLSSocketを新設 → インターフェイスを統一 C++
TLSSocket
• Node-v0.10までの CleartextStreamの替わり
• CleartextStreamのAPIの互換保持
•ちゃんと net.Socketを継承し、APIを完備
• TLS周りの通信に関する新規機能のAPIを追加
より直観的でわかり易くなった。
EventEmitter
Stream
Readable Writable
Duplex
Socket
TLSSocket
AES-NI (Advanced Encryption Standard New Instructions)
• Intel, AMDのCPUに搭載されているAES暗号の処理機能
• 最近のモデルには多く搭載されている。/proc/cpuinfoで確認
• openssl-1.0系に実装済。Node-v0.10.x, v0.11.xでも使える
• 環境変数でAES-NIの有効・無効化してNodeのcryptoのベンチ比較AES-NI有効時 AES-NI無効時
22.8469
Gbit/sec
9.51245
Gbit/sec
AES-NI付きCPUのサーバを選んで使いましょう
AES-NIで2.4倍に性能向上!
PFS(Perfect Forward Secrecy)
•セッション毎に一時的に有効な公開鍵を交換して暗号鍵を共有する方式
•証明書の秘密鍵が危殆化しても過去の通信データを復号化できない。今後主流となる鍵交換方式。
• Node-v0.12から ECDHE, DHEの2種類が利用可能
• ECDHEの方が性能が良いのでそっちを優先指定(デフォルト)
• ECDHEはデフォルトで何もせず利用できる。(prime256v1)
• DHEの利用dhparamファイルを生成と指定が必要。現状十分な強度を持つには2048bit長以上で生成すること。
TLS False StartClientHello
ServerHello
Certificate
ServerHelloDone
ServerKeyExchange
ChangeCipherSpec
Finished
ハンドシェイク完了前に暗号化したアプリデータをフライングで送信
application data
ClientKeyExchange
ChangeCipherSpec
Finished
• TLS接続を高速化する技術 (2RTT→1RTT)
• TLSハンドシェイク完了直前に暗号化したアプリケーションデータをフライングで送信
• Chromeで2010年より先行実装、でも想定外の挙動のため問題頻発。
• TLSハンドシェイク完了前なのでダウングレード攻撃のリスクもあり一旦中止。
• NPNでプロトコル指定、PFS利用で利用可能に。
• IEやSafariなどは別の条件。
NodeでTLS False Startを使うにはNodeはデフォルトでNPN拡張を付与するのでPFSを利用すればクライアントはTLS False Startになる(*)
PFS利用していない時(AES128-GCM-SHA256)
PFS利用している時(ECDHE-RSA-AES128-SHA256)
TLS False Start によるTLSハンドシェイクの高速化
1RTT分
(*) IE, Safariは条件が違います
TLS Ticket•サーバからクライアントにTLS再接続時に利用する
CipherSuite/MasterSecretが含まれた設定データ(チケット)を暗号化して渡す。
•渡したチケット情報はサーバ側では保持しない。
•クライアントは再接続時にチケットをサーバに送信。サーバはチケットデータを復号化し、中のTLS設定情報を利用して通信を再開する。
•チケット鍵が複数のサーバ間で共有できていれば別サーバに行っても大丈夫。(ただし鍵管理が大変)
ClientHello
Sesstion Ticket Ext
ServerHello
SessionTicket Ext
Certificate
ServerHelloDone
ServerKeyExchange
NewSessionTicket
ChangeCipherSpec
Finished
ClientKeyExchange
ChangeCipherSpec
Finished
NodeでTLS Ticketを使う• Node-v0.10で既に使えるが、単一プロセスのみ。
• Node-v0.12ではクラスタの複数プロセス間でチケット鍵が共有可
• Cluster でTLSを使っている方は、今すぐNode-v0.12へ
Node-v0.10.33 のTLSクラスター
Node-v0.12-pre のTLSクラスター
OCSP Stapling• OCSP (Online Certificate Status Protocol):
• 証明書が失効していないか確認するプロトコル
• TLS初期接続時にクライアントが認証局サーバに確認。オーバヘッド
• OCSP Stapling:
• TLSサーバがOCSPレスポンスを証明書とともにクライアントに送信
• クライアントが認証局に確認する必要がない。Web表示の高速化
OCSP
Response
証明書+OCSP
Response
ClientHello +
status_request_v2 ext.
NodeでOCSP Staplingを使う• Node-v0.12では OCSPRequestイベントを新設
• コールバックの引数にOCSPレスポンスデータを渡し、クライアントに送信
• 認証局のOCSPサーバにリクエストするのは結構面倒なので、opensslで事前にリクエストデータを作成すると良い
• OSCPレスポンスデータのキャッシュ管理も必要。更新日時を取得するasn.1パーサも必要。エラー時の処理判断も大切。
• 別途cronでOCSPレスポンスデータを管理する手もある
server.on('OCSPRequest', function(cert, issuer, cb) {
var now = Date.now();
if (now > cache.nextUpdate && !cache.lock) {
cache.lock = true;
cache.der = null;
HandleOCSPrequest(cb);
} else {
var msg = cache.der ? 'cache hit!': 'terminated';
console.log( 'OCSP Response:', msg);
cb(null, cache.der);
}
});
ソースは、https://gist.github.com/shigeki/de5748cc0deb980bcb35
結果は openssl s_client –status –connect site:443 で確認
Dynamic TLS record (setMaxSendFragment)• GoogleのIlya Grigorik氏が推奨しているTLSパラメータのチューニング手法
• 通常TLSに書き込まれるデータ長は、レコードサイズ最大の16Kであることが多い。
• 最初のレスポンスデータの取得は、16Kバイトを受信してからになる。
• 最初のデータを復号化するには10個のTCPパケット(1.5K)を受信が必要。
• TLSのレコードサイズをTCPの1セグメントのサイズに収まるように小さくすれば受信した1個目からデータの復号化が可能になる。
• よって受信したデータの1バイト目が表示される時間の短縮が図られる。
1.5K 1.5K
16K
これ一つで復号可能よ
16K全部受信しないと復号できない
NodeでTLS Dynamic Recordをやるには• Googleサーバでのチューニング方法(ATSで採用済)
• 最初は 1.3Kのレコードサイズ (1500 - 40 (IP) - 20 (TCP) - 40 (TCP options) - TLS overhead (60-100))
• 1Mバイト送信したらデフォルトの16Kに変更。
• 書き込みのアイドルが1秒以上になったら1.3Kに戻す。
• Nodeでは自動的にレコードサイズをチューニングする方法は不採用に
• 替わりに固定的に変更するAPI(setMaxSendFragment)を新設
• 1.3Kで固定すると大きいデータで分割オーバヘッドが高くなる可能性も。
server.on('secureConnection', function(tlsSocket) {
tlsSocket.setMaxSendFragment(1300);
});
小さいTLS Record Sizeの効果(rtt=1000msec時)
MaxSendFragment 16K
default
MaxSendFragment 1.3K
Time To First Byteの高速化
requestStart responseStart
responseStartrequestStart
SPDY, HTTP/2• SPDY
• Googleが開発したWebの表示を高速化するプロトコル(TLSのみ)
• Google/Twitter/Facebook/Yahoo.comで使用中
• Chrome/Firefox/IE10/Safari 多数のブラウザでサポート中
• Nodeで使うなら node-spdyを使いましょう
• HTTP/2
• SPDYをベースとした次期HTTP仕様(TLS利用が中心)
• HTTP/1.1のセマンティクスを互換保持
• 現在仕様化作業の大詰め。来年前半には完了予定
• 現在Firefoxとのテストで利用中のnode-http2が使える
• 注意事項:
• node-v0.10で利用するとTLS要求仕様(AEAD+PFS)が合わず接続できない場合があります。node-v0.12を使いましょう。
• ただ開発は0.10系でやっているので未サポート
• ALPNはまだopensslのベータなためnodeでは使えません。
• 使う場合には私のfork版があります。 https://github.com/shigeki/node/tree/alpn_support
Ethernet
IP(v4/v6)
TCP
TLS
HTTP/2 Frame Layer
HTTP/1.1 Semantics
クライアント サーバ
1つのTCP接続
ストリーム(id:1)
フレーム
フレーム
ストリーム(id:3)
フレーム
フレーム
ストリーム(id:5)
フレーム
フレーム
HTTPリクエストレスポンス
HTTPリクエストレスポンス
HTTPリクエストレスポンス
SPDY, HTTP/2の全二重多重化通信
仮想的なストリームチャンネルを生成して多重化を実現
HTTP Head of Line Blockingを回避
HTTP/2クライアント
画像サーバA
画像サーバB
Reverse
Proxy
HTTP/2多重化通信
レスポンスが速い
レスポンスが遅い
HTTP/1.1クライアント
TCPを6本張れるけど、1本中に同時1リクエストの制限
1本のTCP
SPDYのメリットが一番よくわかるデモ多数の画像を表示して見え方に違いがあるのか? (SPDY vs HTTP/1.1)
1. 少数画像vs多数画像。
2. 3枚目の画像毎に3秒のレスポンス遅延を入れる。
まとめNode-v0.12でTLS通信を最適化する方法はいろいろあります。
用法用量を守って正しくお使いください。
(special thanks to http://www.irasutoya.com/)