秘密鍵をWiresharkに登録してもTLS通信を解読できない?

WiresharkでTLS通信の中身を見るために秘密鍵を登録してみます。秘密鍵はサーバー側に登録されている鍵で、CentOSのApacheの場合はssl.confの「SSLCertificateKeyFile」に記載されている場所にあります。この秘密鍵をWireshark(ubuntu 18.04 LTSにインストール)に登録する手順は以下です。windowsの場合もだいたい同じだと思います。

Wiresharkを立ち上げてメニューバーの「編集」から「設定」を選択する。Wiresharkの設定画面が表示されるので左側の選択欄から「Protocols」→「SSL」と辿ると、画面の上部に RSA keys list があるので隣の「Edit」を押下する。

「Edit」を押下すると SSL Decrypt の画面が表示されるので、左下の「+」ボタンを押下する。IP address、Port、Protocol、Key Fileが入力できる状態になるので、各項目に入力を行う。

IP address・・・サーバーのIPアドレスを指定
Port・・・443と指定
Protocol・・・httpと指定
Key File・・・秘密鍵のファイルを指定
Password・・・パスワードを指定 ※秘密鍵にパスワードがある場合

入力ができたら、SSL Decrypt の画面の「OK」ボタンを押下、および、設定画面の「OK」ボタンを押下する。

[広告]

Wiresharkに秘密鍵が登録できたのでキャプチャをしてみます。キャプチャを開始するにあたり通信のはじめから取得する必要があるため、Wiresharkのキャプチャを開始してからWEBブラウザを立ち上げます。WEBブラウザを立ち上げたあとにWiresharkでキャプチャを開始しても通信のはじめからにはならないので、注意してください。ここで「はじめから」にこだわっているのはWEBブラウザを立ち上げるとサイトとの鍵の交換が始まってしまい最初に行われる鍵交換の部分のキャプチャを取り損なってしまうからです。

それではキャプチャしてみます。実際に取得したキャプチャは以下ですが、アプリケーションデータを見てみると、、、復号されてませんでした(悲)。以下の赤枠で囲った「Encrypted Application Data」の部分です。

ここで何かと調べた結果、どうやらクライアントとサーバーの鍵交換の方式がRSAでないとキャプチャしたデータからWiresharkが共通鍵を取りだせないらしい。RSAでは鍵の元となる情報をクライアント側からサーバー側に送り、相互で同じ共通鍵を生成するとのこと。Wiresharkは秘密鍵を使って、その鍵の元を盗み見して同じ方式で共通鍵を生成しようとする。

[広告]

実際に、どんな鍵交換方式を使っているのかキャプチャから確認してみました。TLSのネゴシエーションでは「Client Hello」と「Server Hello」を使って相互にやりとりする暗号スイートを決めています。暗号スイートとは、鍵交換、認証、共通鍵暗号、ハッシュ関数に何を使うかを決めるためにやり取りする情報です。今回の場合は以下でした。

Client Hello(クライアントからサーバーに提示しているクライアント側で使用可能な暗号スイートのリスト)

Server Hello(クライアントから受けっとた暗号スイートの中で、サーバーが対応していて、且つ、暗号強度が一番強いものを選んでクライアントに返す)

Sever Helloの内容を見てみると、TLSのネゴシエーションで暗号スイートを「TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256」に決めたようです。これは鍵交換方式が「ECDHE」、認証方式が「RSA」、共通鍵暗号方式が「鍵長128ビットのAESのGCMモード」、ハッシュ関数に「SHA-256」を使用するという意味です。つまり、鍵交換方式はECDHEを使っていてRSAではなかったです。そもそもRSAは秘密鍵が漏洩したらパケットをキャプチャして通信の中身を見られてしまうため強度の弱い方式として今はほとんど使われていないみたい。キャプチャしてみたけど、意味なかったです。。

なお、サーバー側がどんな暗号スイートに対応しているかは以下のサイトでチェックできます。ウェブサーバー名を入力して「Submit」ボタンを押下すれば暗号スイート以外にも対応しているSSL/TLSのバージョンなどを提示してくれます。サイトの安全性を総合的に判断してくれます。

https://www.ssllabs.com/ssltest/

今回使ってみたウェブサーバーの暗号スイートの診断結果は以下です。

以前は秘密鍵をWiresharkに登録すればTLS通信の中身を見れたのだろうけれども、今はもうこの方法はほとんど使えないと思います。このブログの別の記事で「ブラウザに共通鍵の情報をファイルに書き出させて、それをWiresharkに読み込ませる」ことでTLS通信の中身を覗いているので、そっちの方法でやってみてください。鍵交換方式が「RSA」以外でも解読できます。

追記:
RSAでは解読できるのにECDHEでは解読できない理由、および、SSLKEYLOGFILEを使えば解読できる理由を考察してみました。こちらまで。

Firewalldにダイレクトルールを設定する。

CentOS7.4のFirewalldにダイレクトルールを設定してみます。以下の方法で設定できると思います。

まず、ダイレクトルールを設定するユーザー定義チェインを作成します。組み込みチェインに直接設定を追加することもできますが管理しやすくするためユーザー定義チェインを利用するほうが良いでしょう。ユーザー定義チェインは「INPUT_custom」という名前で作るとします。

ユーザー定義チェインの作成
# firewall-cmd --direct --add-chain ipv4 filter INPUT_custom

次に、ダイレクトルールをユーザー定義チェインに追加します。例として「SYNflood攻撃と思われる接続を破棄する」ルールの設定です。「-p tcp ! --syn -m state --state NEW -j DROP」の部分がこれに該当します。

ダイレクトルールの作成
# firewall-cmd --direct --add-rule ipv4 filter INPUT_custom 1 -p tcp ! --syn -m state --state NEW -j DROP

最後に、ダイレクトルールを設定したユーザー定義チェインの適用をします。適用先はINPUTチェインです。

ユーザー定義チェインの適用
# firewall-cmd --direct --add-rule ipv4 filter INPUT 1 -j INPUT_custom

以上でFirewalldにダイレクトルールが設定できるはずです。「はずです」と書いているのは、これをまだ試していないからです。このようにやろうと思ったのですが以下の理由により別の方法にしました。

  • Firewalldが導入されているからかなのか、組み込まれているチェインがもともと多く、新たに追加する必要がない気がした。
  • fail2banが「INPUT_direct」のチェインに動的にダイレクトルールを優先度「0」で書き込んでいた。

参考:fail2banを導入したときの記事はこれです。

FirewalldがインストールされているCentOS7.4で「iptables -L」を実行してチェインの内容を確認すると、INPUT、FORWARD、OUTPUT以外にも以下のようなものがありました。

Chain INPUT
Chain FORWARD
Chain OUTPUT
Chain FORWARD_IN_ZONES
Chain FORWARD_IN_ZONES_SOURCE
Chain FORWARD_OUT_ZONES
Chain FORWARD_OUT_ZONES_SOURCE
Chain FORWARD_direct
Chain FWDI_public
Chain FWDI_public_allow
Chain FWDI_public_deny
Chain FWDI_public_log
Chain FWDO_public
Chain FWDO_public_allow
Chain FWDO_public_deny
Chain FWDO_public_log
Chain INPUT_ZONES
Chain INPUT_ZONES_SOURCE
Chain INPUT_direct
Chain IN_public
Chain IN_public_allow
Chain IN_public_deny
Chain IN_public_log
Chain OUTPUT_direct

何がどういう用途なのかはわからないのですが、fail2banを起動させたあとにFirewalldのダイレクトルールの確認、および、iptablesの確認をすると以下でした。

Firewalldのダイレクトルールを確認してみる。


# firewall-cmd --direct --get-all-rules
ipv4 filter INPUT 0 -p tcp -m multiport --dports 50000 -m set --match-set fail2ban-sshd src -j REJECT --reject-with icmp-port-unreachable

iptablesの確認をしてみる。


# iptables -L
 :
Chain INPUT_direct (1 references)
target     prot opt source               destination         
REJECT     tcp  --  anywhere             anywhere             multiport dports 50000 match-set fail2ban-sshd src reject-with icmp-port-unreachable
 :

[広告]

fail2banのダイレクトルールの設定と同じになるようにダイレクトルールは「INPUT_direct」チェインに設定することにします。fail2banのダイレクトルールの優先度は「0」だったので、ここで設定するダイレクトルールの優先度は「1」とします(fail2banのダイレクトルールの優先度のほうを高くしておく)。設定するダイレクトルールは以下です。

①データを持たないパケットの接続を破棄する。
②SYNflood攻撃と思われる接続を破棄する。
③ステルススキャンと思われる接続を破棄する。

※さくらのVPSのサポートページを参考にさせてもらいました。

①データを持たないパケットの接続を破棄する。
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT_direct 1 -p tcp --tcp-flags ALL NONE -j DROP

②SYNflood攻撃と思われる接続を破棄する。
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT_direct 1 -p tcp ! --syn -m state --state NEW -j DROP

③ステルススキャンと思われる接続を破棄する。
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT_direct 1 -p tcp --tcp-flags ALL ALL -j DROP

永年設定の再読み込み。
# firewall-cmd --reload

ダイレクトルールが設定されているか「/etc/firewalld/direct.xml」の中身を確認してみます。

direct.xmlの確認
# cd /etc/firewalld
# cat direct.xml

以下が設定内容です。


<?xml version="1.0" encoding="utf-8"?>
<direct>
  <rule priority="1" table="filter" ipv="ipv4" chain="INPUT_direct">-p tcp --tcp-flags ALL NONE -j DROP</rule>
  <rule priority="1" table="filter" ipv="ipv4" chain="INPUT_direct">-p tcp '!' --syn -m state --state NEW -j DROP</rule>
  <rule priority="1" table="filter" ipv="ipv4" chain="INPUT_direct">-p tcp --tcp-flags ALL ALL -j DROP</rule>
</direct>

また、iptablesの内容も確認してみます。


# iptables -L
 :
Chain INPUT_direct (1 references)
target     prot opt source               destination         
REJECT     tcp  --  anywhere             anywhere             multiport dports 50000 match-set fail2ban-sshd src reject-with icmp-port-unreachable
DROP       tcp  --  anywhere             anywhere             tcp flags:FIN,SYN,RST,PSH,ACK,URG/NONE
DROP       tcp  --  anywhere             anywhere             tcp flags:!FIN,SYN,RST,ACK/SYN state NEW
DROP       tcp  --  anywhere             anywhere             tcp flags:FIN,SYN,RST,PSH,ACK,URG/FIN,SYN,RST,PSH,ACK,URG
 :

ダイレクトルールの設定が追加されています。これで、①〜③のような接続があった場合はFirewalldが遮断をしてくれると思います。

補足:
上記①〜③のパケットを実際に投入したわけではないのでこのルールが機能しているのかはまだ確認できていないのですが(汗)。