【入門編】 DNSクエリログの異常検知とドメイン生成アルゴリズム(DGA)の特定 – インシデントレスポンス & フォレンジック(DFIR)防御ガイド

こんにちは!セキュリティの世界へようこそ。
日々の開発やインフラの管理、本当にお疲れ様です。「なんだかサーバーの調子がおかしいな」「見慣れない通信がたくさんあるな」と感じたことはありませんよね?

今回は、サイバー攻撃者が忍び込んできたときに使う、ちょっとズルくて巧妙な手口――「DGA(ドメイン生成アルゴリズム)」と、それを見つけ出すための「DNSクエリログの解析」について、身近な例えを交えながら優しく紐解いていきたいと思います。

難しい言葉が出てきても、「一歩ずつ対策を学んでいきましょう!」という気持ちで進めていきますので、どうぞリラックスして読んでくださいね。

—

1. 家の鍵と「泥棒の合言葉」に例えるサイバー攻撃

まずは、私たちの身近な防犯に例えて考えてみましょう。

皆さんの家には、頑丈な玄関の鍵がありますよね。泥棒が家に侵入するとき、まずはその鍵をこじ開けようとします。セキュリティの世界で言えば、これは「ファイアウォール」や「サーバーへの不正アクセス」にあたります。

しかし、最近の泥棒はとても頭が良いのです。たとえ家に侵入できなくても、「家の中の誰かと外の仲間(悪い親分)がこっそり連絡を取り合えるようにする」という裏口を作ろうとします。これが、サイバー攻撃でよく聞く「C2(Command and Control)サーバーとの通信」です。

通常、警察(セキュリティソフト)は、泥棒が連絡を取り合っている「アジトの住所(特定のウェブサイトのURLやIPアドレス)」をあらかじめ把握していて、「この住所宛ての郵便や電話はすべてブロック!」と見張っています。

ここで、泥棒が使うのが「DGA(Domain Generation Algorithm:ドメイン生成アルゴリズム)」というずる賢い仕組みです。

  • 泥棒の作戦:

毎日、あるいは数分おきに「今日連絡するアジトの住所」を、あらかじめ決めたルール(計算式)に従って自動でガラリと変えてしまうのです。

  • 警察の悩み:

「昨日まで『 abc.com 』だったアジトが、今日は『 xqz789.net 』に変わり、明日は誰も聞いたことがない『 mnp456.org 』に変わる……! 住所が毎回変わるから、張り込みが追いつかない!」

この「コロコロと変わる怪しい住所(ドメイン名)」を、マルウェアはインターネットの電話帳である「DNS」に対して、大量に問い合せ(クエリを送信)続けます。この不自然なやり取りを見破るのが、今回の主役である「DNSクエリログの異常検知」なんです。

—

2. 現場で何が起きている? DNSクエリログの「不自然なざわざわ」

では、実際に企業のネットワークやサーバーの裏側で何が起きているのか、もう少し詳しく見ていきましょう。

パソコンやサーバーが「ねえ、example.comってどこにあるの?」とDNSサーバーに尋ねることを「DNSクエリ」と呼びます。通常、私たちが日常的にアクセスするドメインは、人間にも読みやすい意味のある名前(google.comやgithub.comなど)をしていますよね。

しかし、先ほどお話ししたDGAが作り出すドメインは、人間が見ると「猫がキーボードの上を歩いて作ったような、意味不明な文字列」になります。例えばこんな感じです。

  • k9s8d7f6g5h4j3k2.com
  • qazxswedcvfrtgbnh.net
  • xypq-98123746-zz.org

これらは、人間だけでなく、通常のシステムにとっても「そんな名前のウェブサイト、聞いたことないよ!」というものばかりです。

「NXDOMAIN(そんな名前ないよ!)」の嵐に注目せよ

DGAに感染したマルウェアは、今日のアジトの住所がどこにあるか分からないため、手当たり次第に数百、数千という「架空のドメイン名」を作っては、DNSサーバーに「この住所知ってる?」と聞きまくります。

DNSサーバーは当然、「そんなドメインは存在しませんよ」と答えを返します。このエラー応答のことを、専門用語で「NXDOMAIN(Non-Existent Domain)」と呼びます。

インシデントレスポンスの現場では、この「NXDOMAINの応答が、特定の端末から異常なほど大量に発生しているログ」を見つけることが、DGAやマルウェア感染を発見する最大の手がかりになるのです。

—

3. 実践! PythonでDNSクエリログ(NXDOMAIN)を解析してみよう

「理屈は分かったけれど、実際にどうやって見つければいいの?」という声にお応えして、シンプルなログ解析のスクリプトを用意しました。

実際の現場では、数万行もある巨大なDNSのアクセスログ(テキスト形式やJSON形式)から、「おや、このパソコン、変な問い合わせをたくさんしていないか?」と探すことになります。

以下のPythonコードは、DNSのログから「NXDOMAIN(存在しないドメインへの問い合わせ)」を抽出し、特定の端末が怪しい動きをしていない集計するサンプルです。コメントを丁寧に書いたので、ぜひ雰囲気を掴んでみてくださいね。

from collections import Counter

# 模擬的なDNSクエリログのリスト
# 実際には、SyslogやDNSサーバー(BINDやWindows DNSなど)のログファイルから読み込みます
dns_query_logs = [
    {"client_ip": "192.168.1.50", "query_domain": "google.com", "response_code": "NOERROR"},
    {"client_ip": "192.168.1.105", "query_domain": "k9s8d7f6g5h4j3k2.com", "response_code": "NXDOMAIN"},
    {"client_ip": "192.168.1.105", "query_domain": "qazxswedcvfrtgbnh.net", "response_code": "NXDOMAIN"},
    {"client_ip": "192.168.1.50", "query_domain": "github.com", "response_code": "NOERROR"},
    {"client_ip": "192.168.1.105", "query_domain": "xypq98123746zz.org", "response_code": "NXDOMAIN"},
    {"client_ip": "192.168.1.105", "query_domain": "randomstring12345.biz", "response_code": "NXDOMAIN"},
    {"client_ip": "192.168.1.105", "query_domain": "anotherfake.info", "response_code": "NXDOMAIN"},
]

def detect_suspicious_dns_activity(logs, threshold=3):
    """
    DNSログからNXDOMAIN(存在しないドメイン)への問い合わせが多い端末を検知する関数
    :param logs: DNSログのリスト
    :param threshold: 異常とみなすNXDOMAINの閾値(今回は簡単のため3回以上に設定)
    """
    print("[*] DNSクエリログの解析を開始します...\n")
    
    # NXDOMAINを発生させているクライアントIPごとにカウントするための辞書
    nxdomain_counter = Counter()

    for log in logs:
        # 応答コードが NXDOMAIN(ドメインが存在しない)の場合のみ集計対象にする
        if log["response_code"] == "NXDOMAIN":
            client_ip = log["client_ip"]
            nxdomain_counter[client_ip] += 1
            print(f"[警告] 存在しないドメインへの問い合わせを検知: IP = {client_ip}, ドメイン = {log['query_domain']}")

    print("\n--- 解析結果サマリー ---")
    # 閾値を超えて怪しい動きをしている端末をチェック
    for ip, count in nxdomain_counter.items():
        if count >= threshold:
            print(f"[!] 🚨 危険: クライアントIP [{ip}] から異常な数のNXDOMAIN({count}回)を検知しました!")
            print(f"    -> この端末はマルウェア(DGA)に感染している可能性があります。すぐにネットワークから隔離を検討してください。\n")
        else:
            print(f"[-] クライアントIP [{ip}] のエラー回数は許容範囲内です({count}回)。")

# スクリプトの実行
if __name__ == "__main__":
    detect_suspicious_dns_activity(dns_query_logs)

このコードのポイント

  • response_code == "NXDOMAIN" という条件で、泥棒が適当に作り出した「ハズレの住所」への問い合わせだけを綺麗にフィルタリングしています。
  • 特定のパソコン(今回の例では 192.168.1.105)だけが何度もエラーを出している場合、その端末のユーザーが偶然タイポ(入力ミス)を頻発している可能性もありますが、インシデントレスポンスの現場では「マルウェアによるC2通信の試行(DGA)」を強く疑ってかかります。

—

4. 新人のIT担当者・開発者が今日からできる現実的な防犯対策

「ログの解析ってなんだか難しそう……」と思った方もいらっしゃるかもしれません。でも、いきなり高度なツールを使いこなす必要はありません。身近なところから、一歩ずつ対策を学んでいきましょう!

1. DNSログを「捨てずに残す」設定にする
会社のルーターや社内DNSサーバー(Windows ServerのDNSやBINDなど)のログ機能が、デフォルトでオフになっていないか確認してみましょう。ログがなければ、後から泥棒の足跡を追うことができません。「ログを残すこと」がすべての第一歩です。
2. 不審なドメイン名の特徴(エントロピー)を知る
人間が考えたドメイン名(例: corporate-portal.com)と、機械がランダムに作った文字列(例: x8k2j9s1d7f6g.com)は、文字のバラエティ度合い(情報エントロピー)が異なります。「妙にランダムで長いドメインへのアクセスが多いな」と気づける直感を、日頃のニュースや技術記事読みを通じて養っていきましょう。
3. 信頼できるセキュリティ製品やクラウドDNSを活用する
企業でインフラを管理する場合、社内のPCが直接インターネットのよく分からないDNSサーバーを見に行くのではなく、セキュリティ機能付きのDNSサービス(Cisco UmbrellaやCloudflare Gatewayなど、既知の悪意あるドメインやDGAを自動でブロックしてくれるサービス)を通すように設定するのが、最も確実で効果的な防犯対策になります。

—

おわりに

サイバー攻撃の手口は日々進化していますが、攻撃者が通信を行う以上、必ずどこかに「不自然な足跡(ログ)」が残ります。

最初はチンプンカンプンに見えたログの羅列も、「あ、これは泥棒が合い言葉を探している時のざわざわだな」とストーリーとして理解できるようになると、インシデントレスポンスの仕事がぐっと面白くなります。

焦らず、一歩ずつ、あなたの守るシステムを強固なものに育てていきましょう。それでは、また次回のセキュリティ解説でお会いしましょう!

コメント

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