こんにちは!技術メディアの主筆ライターであり、日々ネットワークを飛び交うパケットたちと対話しているセキュリティスペシャリストです。
突然ですが、みなさんは「プライバシーを守るための暗号化通信」が、実はサイバー攻撃者にとっても「絶好の隠れみの」になっていることをご存じでしょうか?
現在、Webサイトの閲覧(HTTPS)をはじめ、インターネット上の通信のほとんどが暗号化されています。これにより、第三者に通信内容を盗み見られる心配はなくなりました。しかしそれは同時に、「通信の中に隠れているマルウェアの悪だくみ(C2サーバーとの通信)も、外からは見えなくなってしまった」ということを意味します。
「中身が見えないなら、どうやって攻撃を防げばいいの?」と不安になりますよね。
そこで登場するのが、今回ご紹介する「JA3/JA3Sフィンガープリンティング」という画期的な技術です。
この技術を使えば、暗号をわざわざ解除(復号)しなくても、通信の「ある特徴」を見るだけで、「おや、この通信は普通のブラウザではなく、マルウェアだな!」と見破ることができるのです。
今回は、インフラやネットワークに初めて触れる方に向けて、この魔法のような仕組みを、郵便配達や日常の身近な例えを交えながら、一歩ずつ丁寧に紐解いていきます。一緒に楽しく学んでいきましょう!
—
1. 暗号化の前の「はじめまして」:TLSハンドシェイク
暗号化通信(主にTLSと呼ばれるプロトコル)を始める前、パソコン(クライアント)とWebサーバーの間では、「TLSハンドシェイク(握手)」と呼ばれる事前の挨拶が行われます。
これを現実世界の「郵便配達」や「ビジネスの商談」に例えてみましょう。
あなたが海外の取引先に、極秘の書類を鍵付きの箱(暗号化通信)で送りたいとします。でも、相手がどんな鍵を持っているのか、どんな言語(暗号化の方式)で話せるのかが分かりません。そこで、まずは普通の手紙(暗号化されていない平文)で、次のような自己紹介を送ります。
> 「はじめまして! 私は日本語と英語が話せます。
> 鍵はAタイプとBタイプが使えます。
> あと、便利なオプション機能として、速達も使えますよ!」
この、一番最初に送る自己紹介の手紙のことを、ネットワークの世界ではClient Hello(クライアント・ハロー)と呼びます。
このClient Helloには、暗号化される前の「素のデータ」が書かれているため、ネットワークの通り道(ルーターやファイアウォールなど)から丸見えになっています。ここが今回の重要なポイントです!
—
2. JA3フィンガープリントとは?:「自己紹介のクセ」を見抜く
「中身が見えるなら、そこにマルウェアの名前が書いてあるの?」というと、残念ながらそんなに甘くはありません。手紙の差出人名には「Google Chrome」や「Microsoft Edge」といった、無害なブラウザのふりをした名前(ユーザーエージェント)が書かれていることがほとんどだからです。
しかし、嘘をつけない部分があります。それが「自己紹介の並べ方や、使える機能の組み合わせ(クセ)」です。
郵便に例える「クセ」の正体
想像してみてください。
同じ「日本語と英語が使えます」という内容でも、人によって書き方にクセが出ますよね。
- Aさん:「日本語、英語が話せます。好きなオプションは速達です。」
- Bさん:「英語と日本語に対応。オプション:速達。」
- マルウェアC:「日本語のみ。オプションは無し。」
Google ChromeやSafariなどの本物のブラウザは、とても洗練された最新の自己紹介リストを持っています。一方で、マルウェアや攻撃者が使う通信ツール(C2通信を行うライブラリなど)は、プログラムを軽量にするために、古い方式や、特定のシンプルな設定の組み合わせで自己紹介を送る傾向があります。
この、Client Helloに含まれる5つの特定の項目(暗号のバージョンや、使える暗号のリストなど)を順番に並べ、カンマで繋いだ文字列を作ります。
(イメージ例)
バージョン,暗号リスト,拡張機能リスト,楕円曲線リスト,楕円曲線形式
↓
「771,49195-49199-52393,0-23-65281,29-23-24,0」
このままだと長くてバラバラなので、コンピュータが扱いやすいように、「MD5」という技術を使って32文字の英数字(ハッシュ値)にギュッと変換します。
この、自己紹介のクセを凝縮した32文字のIDこそが、「JA3フィンガープリント(ハッシュ値)」です!
- Google ChromeのJA3:
b32309a26938fd4c5030f4e047c64911(例:無害) - マルウェア「Emotet」のJA3:
4c79a1d32...(例:危険!)
このように、通信の暗号を解かなくても、「あ、この自己紹介のクセ(JA3)は、悪名高いマルウェアのものだ!」と一瞬で見破ることができるのです。
—
3. JA3Sとは?:サーバー側の「お返事のクセ」で精度を上げる
しかし、攻撃者もバカではありません。
「JA3で見破られるなら、Google Chromeと全く同じ自己紹介(Client Hello)を真似して送るようにマルウェアを改造しよう」と考えます。
これに対抗するために作られたのが、相棒となる「JA3S」です。
先ほどの郵便の例に戻りましょう。
偽のGoogle Chrome(中身はマルウェア)が、悪意あるサーバー(C2サーバー)に「はじめまして」の手紙を送りました。
悪意あるサーバーは、本物のGoogleやYahoo!のサーバーとは違って、怪しい闇のサービスしか提供していません。そのため、返ってくる返事(Server Hello)のクセも、普通のサーバーとは大きく異なります。
- JA3: クライアント(PC側)の自己紹介のクセ
- JA3S: サーバー側のお返事のクセ
この2つをセット(JA3 + JA3S)で監視することで、「Google Chromeのフリをして、闇のサーバーと怪しい暗号の組み合わせで握手(ハンドシェイク)を交わしている通信」を、ほぼ確実に特定できるようになります。
—
4. 【実践】Pythonで学ぶ!JA3フィンガープリントの抽出イメージ
「なんとなく仕組みは分かったけれど、実際のプログラムではどうやって動いているの?」と気になりますよね。
ここでは、ネットワークのパケット(通信の塊)を解析するPythonのライブラリを使って、パケットからJA3を計算する処理の概念的なコードを見てみましょう。
実務でそのまま参考にするための、丁寧なコメント付きのサンプルコードです。
# 必要なライブラリをインポートします
# ※実際に動かすには pip install scapy dpkt などのライブラリが必要です
import hashlib
from scapy.all import rdpcap
from scapy.layers.tls.handshake import TLSClientHello
def calculate_ja3_from_packet(pcap_file_path):
"""
保存された通信データ(pcapファイル)からTLS Client Helloを探し、
JA3ハッシュ値を擬似的に算出する関数です。
"""
print(f"[+] 解析を開始します: {pcap_file_path}")
# 通信ログファイルを読み込みます
packets = rdpcap(pcap_file_path)
for packet in packets:
# パケットの中に TLS Client Hello(最初の自己紹介)が含まれているか確認します
if packet.haslayer(TLSClientHello):
client_hello = packet[TLSClientHello]
# JA3の元となる5つの要素を抽出します
# 1. TLSのバージョン (例: TLS 1.2 = 0x0303 -> 十進数で 771)
tls_version = str(client_hello.version)
# 2. サポートしている暗号スイートのリスト
ciphers = "-".join([str(c) for c in client_hello.ciphers])
# 3. 拡張機能(Extensions)のリスト
extensions = "-".join([str(ext.type) for ext in client_hello.ext]) if client_hello.ext else ""
# 4. サポートしている楕円曲線のリスト(Supported Groups)
# (※パケットの内部から該当する拡張を探します)
supported_groups = "23-24-25" # 簡易的なダミー値
# 5. 楕円曲線のフォーマット形式
ec_formats = "0" # 簡易的なダミー値
# 5つの要素をカンマ「,」で連結して、JA3の「素の文字列」を作ります
ja3_string = f"{tls_version},{ciphers},{extensions},{supported_groups},{ec_formats}"
print(f"[!] JA3の素の文字列を作成しました:\n -> {ja3_string}")
# MD5ハッシュ関数を使って、32文字の暗号文(フィンガープリント)に変換します
ja3_hash = hashlib.md5(ja3_string.encode('utf-8')).hexdigest()
print(f"[★] 算出されたJA3ハッシュ値: {ja3_hash}")
return ja3_hash
print("[-] TLS Client Hello パケットが見つかりませんでした。")
return None
# 実行イメージ(実際にお手元の環境で試す際の呼び出し方)
# calculate_ja3_from_packet("traffic_sample.pcap")
このように、パケットから特定の値を抜き出して一本の紐にし、それをハッシュ化するだけで、通信の「指紋(フィンガープリント)」が手に入るのです。
—
5. セキュリティ製品での活用例(Suricataのルール設定)
実際の企業のネットワーク防衛の現場では、Suricata(スリカタ)やZeek(ジーク)といった、ネットワーク監視システム(IDS/IPS)が導入されています。
これらのシステムでは、先ほど計算した「悪意あるJA3ハッシュ値」を登録しておくことで、該当する通信が通り抜けた瞬間にアラートを鳴らすことができます。
以下は、Suricataで使われる防御ルールの簡単な例です。
# マルウェア(例:Cobalt Strike)のJA3ハッシュ値を検知して警告するルール
alert tls $HOME_NET any -> $EXTERNAL_NET any ( \
msg:"[WARNING] マルウェア固有の通信(JA3)を検知しました!C2通信の疑いあり"; \
flow:established,to_server; \
# ここで不審なJA3ハッシュ値を指定します
ja3.hash; content:"e7ef139b3f71c4c9d31139e83cf31215"; \
classtype:trojan-activity; \
sid:1000001; \
rev:1; \
)
このルールがセットされていると、社内のパソコンがマルウェアに感染し、外部のコントロールサーバー(C2サーバー)に暗号化通信で「準備完了!」と連絡を取ろうとした瞬間、ゲートウェイで「あ!このJA3はブラックリストに載っている!」と検知し、通信を遮断(ドロップ)できるようになります。
—
まとめ:一歩ずつ理解を深めていきましょう!
暗号化通信の裏に潜むサイバー攻撃に対抗するための「JA3/JA3Sフィンガープリンティング」について、イメージは湧きましたでしょうか?
最後におさらいをしましょう。
- TLSハンドシェイク: 暗号化を始める前の「はじめまして」の挨拶。
- JA3: クライアントが送る「自己紹介のクセ(Client Hello)」を32文字にしたもの。
- JA3S: サーバーが返す「お返事のクセ(Server Hello)」を32文字にしたもの。
- メリット: 暗号を解読しなくても、マルウェア固有の「通信の指紋」を特定して防御できる!
ネットワークセキュリティの世界は、一見すると小難しい英単語や数字だらけに見えますが、こうして「人間同士のやり取り」に例えてみると、とても人間味があって面白い技術だと気づいていただけるはずです。
ゼロトラスト(すべての通信を疑い、検証する)の時代において、この「指紋認証」のようなアプローチはますます重要になっています。
焦らず、一歩ずつ、パケットの気持ちになりながらセキュリティの奥深い世界を楽しんでいきましょう。あなたのエンジニアとしての第一歩を応援しています!
コメント