【入門編】 DNSトンネリング検知におけるTXTレコードおよびNULLレコードのパケット長・エントロピー解析 – サイバーセキュリティとプライバシー保護実践ガイド

こんにちは!インフラの裏側を覗くのが大好きな、技術メディアの主筆ライターです。

皆さんは「DNS」という言葉を聞いたとき、どんな仕組みを思い浮かべますか?「google.com と打ち込んだら、裏側でIPアドレスに変換してくれる便利な電話帳みたいなやつでしょ?」と思った方、大正解です。普段私たちが何気なく使っているWebブラウジングは、このDNSというシステムのおかげで成り立っています。

しかし、セキュリティの現場、特に企業のネットワークを脅威から守る最前線では、このDNSが「悪意あるデータの隠れ家」として使われるケースが後を絶ちません。今回は、攻撃者が好んで使う「DNSトンネリング」という手口を、私たちがどうやって見破っているのか、その核心に迫る技術を優しく紐解いていきましょう。

難しそうな専門用語が出てきても、「一歩ずつ理解していきましょう!」と手を取り合って解説しますので、どうぞ気楽についてきてくださいね!

—

1. 身近な例えで理解する「DNSトンネリング」の正体

まずは、DNSの本来の姿と、攻撃者が行う「トンネリング」の仕組みを身近な例えで考えてみましょう。

想像してみてください。あなたは今、厳重なセキュリティで守られた巨大なオフィスビル(企業の内部ネットワーク)の中にいます。このビルから外へ手紙を送るときは、必ず1階の受付を通らなければなりません。受付の警備員さんは厳しく目を光らせており、怪しい荷物や機密情報の入ったUSBメモリなどは絶対に外へ持ち出させない仕組みになっています。

「これなら安心だね!」と思いますよね。ところが、攻撃者はこう考えました。

> 「よし、手紙の外側を『DNSの問い合わせ』という形に偽装して、封筒の裏にこっそり秘密のデータを書き込んで外へ放り投げよう。受付の警備員さんは『ただの電話番号の問い合わせだな』とスルーするはずだ」

これがDNSトンネリングのカラクリです。DNSの通信は、インターネットの仕組み上、ほとんどの企業ネットワークで「外へ出ていって良い正当な通信」として許可されています。攻撃者はこの「お墨付き」を悪用し、企業の機密データを細切れにして、DNSの問い合わせ(ドメイン名)の中に隠して外のサーバーへ送り出すのです。

—

2. センサーが怪しい通信を嗅ぎ分ける「2つの武器」

社内ネットワークの出口に置かれたセキュリティセンサー(ネットワークセンサー)は、このずる賢い手口をどうやって見つけ出しているのでしょうか?

ここで今回の主役である 「パケット長」 と 「エントロピー解析」 が登場します。センサーは、DNSパケットという「封筒」の見た目と中身のゴチャゴチャ具合をチェックしています。

武器①:パケットの長さ(レングス)を見る

通常のDNSの問い合わせは、「www.example.com」のように、人間にとっても読みやすい、そこまで長くない文字数で構成されています。

しかし、DNSトンネリングでは、機密データを詰め込むためにドメイン名が異常に長くなります。郵便配達の例で言えば、普通のハガキのつもりで受け取ったら、何十メートルもの長さがある巻物が届いたようなものです。「おいおい、この封筒、やけにデカくて長いぞ?」とセンサーが気づくきっかけになります。特に TXTレコード や NULLレコード と呼ばれる、比較的大きなデータを乗せられる仕組みが悪用されやすいため、センサーはここを厳しく監視しています。

武器②:エントロピー(ランダム性)を見る

もう一つの武器が「エントロピー解析」です。少し難しそうな言葉ですが、要するに「どれくらいデタラメでランダムなかたまりか」を数値化する仕組みです。

例えば、日本語の文章や英語の単語は、文字の並び方に一定の規則性があります(「the」の次には母音が来やすい、など)。
しかし、攻撃者がデータを送り出すときは、普通は暗号化したり圧縮したりしてバラバラの文字列(例:a1B7x9Zq-mKp3...)に変えてしまいます。この「人間が読めない、規則性のないメチャクチャな文字列」は、情報科学の世界ではエントロピーが高いと表現されます。

センサーは、「なんかこのDNSの問い合わせ、英数字がデタラメに並びすぎていて、人間の言葉や普通のURLっぽくないぞ?」という不自然さを、数学的な計算によって瞬時に嗅ぎ分けるのです。

—

3. 実践!Pythonで体験するパケットのエントロピー・長さ解析

「理屈はわかったけれど、実際にどうやってプログラムで判定しているの?」という疑問にお答えするために、センサーの頭脳の一部を切り出したようなシンプルなPythonスクリプトを見てみましょう。

実際の開発やインフラの監視スクリプトでもそのまま参考にできるよう、丁寧に日本語コメントを添えました。

import math
import dns.message
import dns.rdatatype

def calculate_entropy(data: str) -> float:
    """
    文字列のエントロピー(ランダム性)を計算する関数です。
    値が大きいほど、データがランダム(=暗号化や圧縮された不審なデータ)であることを示します。
    """
    if not data:
        return 0.0
    
    # 文字列全体の長さを取得
    length = len(data)
    entropy = 0.0
    
    # 各文字の出現頻度をカウントし、シャノンエントロピーの計算式に当てはめます
    for char in set(data):
        p_x = data.count(char) / length
        entropy -= p_x * math.log2(p_x)
        
    return entropy

def inspect_dns_query(packet_bytes: bytes):
    """
    流れてきたDNSパケットを解析し、DNSトンネリングの疑いがないか判定する関数です。
    """
    try:
        # バイト列のパケットをDNSメッセージとしてデコードします
        msg = dns.message.from_wire(packet_bytes)
        
        for question in msg.question:
            qname = str(question.name) # 問い合わせされたドメイン名
            qtype = dns.rdatatype.to_text(question.rdtype) # レコードタイプ (A, TXT, NULLなど)
            
            # 1. パケット長(ドメイン名の長さ)のチェック
            # 通常のドメイン名より異常に長い場合(例: 50文字超)を警戒します
            is_long = len(qname) > 50
            
            # 2. エントロピー(ランダム性)のチェック
            # ドメイン名からドット(.)を除いた部分のランダム性を計算します
            raw_string = qname.replace('.', '')
            entropy = calculate_entropy(raw_string)
            
            # 判定閾値(この値は環境やチューニングによって調整します)
            is_high_entropy = entropy > 4.0
            
            print(f"[*] 検査中 -> 問い合わせ先: {qname} (タイプ: {qtype})")
            print(f"    - 文字数: {len(qname)}文字")
            print(f"    - エントロピー値: {entropy:.2f}")
            
            # 警告判定
            if is_long and is_high_entropy:
                print("    [!] 【警告】DNSトンネリングの可能性があります!即座に遮断を検討してください。")
            else:
                print("    [-] 正常な通信と判断します。")
                
    except Exception as e:
        print(f"[-] パケットの解析中にエラーが発生しました: {e}")

# --- 実行テストの例 ---
if __name__ == "__main__":
    # シミュレーション用に、正常なDNSクエリと不審なDNSクエリを模したパケットを作ってみます
    # (※実際の運用ではパケットキャプチャライブラリ等からバイト列を取得して渡します)
    print("--- セキュリティセンサーのシミュレーションを開始します ---\n")
    
    # ※ここではイメージしやすいよう擬似的に文字列を渡す簡易テストを行っています
    test_qname_normal = "www.google.com."
    test_qname_suspicious = "a3f8b9c2d1e4f5a6b7c8d9e0f1a2b3c4.malicious-tunnel.com."
    
    print(f"テスト1 (正常系): {test_qname_normal}")
    print(f"  -> 文字数: {len(test_qname_normal)}, エントロピー: {calculate_entropy(test_qname_normal.replace('.', '')):.2f}")
    
    print(f"\nテスト2 (異常系・トンネリング疑い): {test_qname_suspicious}")
    print(f"  -> 文字数: {len(test_qname_suspicious)}, エントロピー: {calculate_entropy(test_qname_suspicious.replace('.', '')):.2f}")
    print("  [!] 【警告】高いエントロピーと長大なペイロードを検出しました!")

このように、プログラムは「長さ」と「エントロピー」という2つの物差しを使って、人間が目で見て気づくよりもはるかに高速に、不正なトンネリング通信を見つけ出しているのです。

—

4. 現場のエンジニアが知っておくべきチューニングのコツ

最後に、実際のネットワーク環境にこうしたセンサーやセキュリティ製品(IDS/IPSや次世代ファイアウォールなど)を導入・運用する際の、現場ならではの泥臭いコツをお伝えします。

1. 誤検知(フォールスポジティブ)との戦い
最新のCDN(コンテンツ配信ネットワーク)や、複雑なクラウドサービスの仕組みの中には、あえてユニークなハッシュ値を含んだ長めのドメイン名を使うものが少なくありません。エントロピーの閾値を厳しくしすぎると、社内の正当な業務アプリまでブロックしてしまう「大惨事」になりかねません。「自社でよく使われる正当なトラフィックのベースライン(基準)」をしっかり把握してから閾値を設定することが鉄則です。
2. TXTレコードとNULLレコードの重点監視
通常のWeb閲覧では、あまり TXTレコード や NULLレコード を大量にやり取りすることはありません。攻撃者はデータを効率よく詰め込むためにこれらを好むため、センサーの設定では「これらのレコードタイプに対する文字長制限」を通常の Aレコード よりも厳しめに設定するのが効果的です。

—

おわりに

今回は、DNSトンネリングの裏側と、それを暴く「パケット長・エントロピー解析」の仕組みについて解説しました。

一見すると難解なセキュリティ技術も、「郵便配達と怪しい封筒」という身近な例えに置き換えてみると、センサーがやっている役割がぐっと身近に感じられたのではないでしょうか。ネットワークの裏側で静かに、しかし力強くデータを守るエンジニアたちの仕事は、パケットという目に見えない旅人たちの安全を守る、まさに現代の交通整理そのものです。

今回の知識が、皆さんの日々のインフラ運用やセキュリティ学習の小さなスパイスになれば幸いです。それではまた、次の技術の深掘りでお会いしましょう!

コメント

タイトルとURLをコピーしました