昨今、セキュリティ上の問題から、TLS 1.2以前のバージョンを利用するブラウザのサポート終了のニュースを目にすることが増えてきました。
Yahoo!セキュリティセンター | セキュリティ強化のお知らせ
Adobe Sign:TLS 1.0 および 1.1 のサポート終了
サポート終了にあたり、WEBサービス提供者としてはユーザーに何らかの方法で、ユーザーの対応の有無を通知したい場合がほとんどだと思います。このエントリでは先日、ngx_mrubyに追加された、 tls_version
メソッドを利用して、クライアントのサポートするTLSバージョンに応じて表示するサイトのコンテンツを変更する方法を紹介したいと思います。
今回の例は下記のような構成を想定しています。HTTPS(TLS)通信はNginxで終端し、後段のOriginサーバへHTTPでリバースプロキシする構成です。
この構成の場合に、TLS1.2、1.3であれば、 /tls_ok
にリバースプロキシし、 該当しなければ /tls_ng
にリバースプロキシするコンフィグ例は下記になります。
mruby_ssl_handshake_handler_code ' ssl = Nginx::SSL.new Userdata.new.ssl_tls_version = ssl.tls_version '; location / { mruby_set_code $proxy_path " Userdata.new.ssl_tls_version =~ /TLSv1\.(2|3)/ ? 'ok' : 'ng' "; proxy_pass http://<origin_ip>/tls_$proxy_path; }
コンフィグの内容としてはSSLハンドシェーク中にクライアントのTLSバージョンを取得し、それをSSLハンドシェークの処理のコンテキスト外に値を持ち運ぶために、mruby-userdataのクラスであるUserDataに値を格納しています。
locationディレクティブにおいて、UserDataからTLSバージョンを取り出し、値を評価し、 ok
か ng
をNginxの変数である $proxy_path
に格納しています。そして、 $proxy_path
の値を利用して、プロキシ先のパスを /tls_ok
か /tls_ng
に振り分けるという実装です。
なお、 tls_version
の戻り値はOpenSSLのSSL_get_version関数の戻り値である、次のいずれかです。
- SSLv3
- TLSv1
- TLSv1.1
- TLSv1.2
- TLSv1.3
- unknown
例えば、下記のような実装でも良いとは思います。
Userdata.new.ssl_tls_version.gsub(/TLSv/, '').to_f >= 1.2 ? 'ok' : 'ng'
実行結果は下記のようになります。
irb(main):007:0> "SSLv3".gsub(/TLSv/, '').to_f => 0.0 irb(main):008:0> "TLSv1".gsub(/TLSv/, '').to_f => 1.0 irb(main):009:0> "TLSv1.2".gsub(/TLSv/, '').to_f => 1.2 irb(main):010:0> "unknown".gsub(/TLSv/, '').to_f => 0.0 irb(main):011:0>
後者のほうが今後1.4が出た場合も何もしなくていいので便利かもしれません。
追記(2018/08/07)
ngx_mrubyを利用していない場合でも $ssl_protocol
に同等の値が入るので、ngx_mrubyを未導入の場合はこちらの値を利用すると良いでしょう。
まとめ
今後、業界内で更に加速するTLS 1.2以下のサポート終了にあたり、 tls_version
のAPIを利用すればミドウェアのレイヤで簡単にクライアントを振り分けることが可能です。ngx_mrubyを利用すればこれだけではなく、下記の資料のように多くの問題をプラガブルに解決できる仕組みが揃っているので、ぜひこの機会に導入を考えてみてはいかがでしょうか。