【実務・中級編】 Azure NSG(ネットワークセキュリティグループ)のフローログ分析と異常通信の検知 – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

なぜ「NSGフローログ」を眺めるだけで終わらせてはいけないのか

現場でよく見る光景がある。「AzureのNSGフローログを有効にしました」と胸を張るエンジニアたちだ。だが、彼らの多くはログをストレージに溜め込むだけで、その中身を「分析」していない。

いいか、攻撃者は君たちが寝静まった深夜、あるいは祝日の午後にやってくる。彼らはポートスキャンで脆弱なポイントを執拗に探し、侵入の足がかりを掴もうとする。NSGフローログは、単なるストレージの肥やしではない。君たちのインフラを守るための「最前線のレーダー」なんだ。

今日は、ただの記録係から「防御のプロ」へ脱皮するための、KQL(Kusto Query Language)を用いた異常検知と、その先にある自動防御の実装について話そう。

—

攻撃者の視点:なぜポートスキャンから始まるのか

攻撃者が最初に行うのは、いわゆる「Reconnaissance(偵察)」だ。彼らは特定のIP範囲に対し、片っ端からポートスキャンを仕掛ける。もし、SSH(22)やRDP(3389)、あるいはデータベースのポートが不用意にインターネット公開されていれば、そこを起点にブルートフォース攻撃やエクスプロイトコードの注入が始まる。

これを防ぐためには、「意図しない通信の可視化」と「通信量(フロー数)の異常検知」が鍵になる。

—

ステップ1:KQLで異常なスキャンを炙り出す

Log Analyticsに転送されたNSGフローログを使って、短時間に多数の宛先ポートへアクセスしている「スキャナー」を特定しよう。以下のKQLは、直近1時間で10以上の異なるポートにアクセスした送信元IPを抽出するものだ。

// 異常なポートスキャンを検知するクエリ
AzureNetworkAnalytics_CL
| where TimeGenerated > ago(1h)
| where FlowStatus_s == "D" // 拒否された通信を追うことで攻撃の意図が見える
| summarize PortCount = dcount(destPort_d) by srcIP_s
| where PortCount > 10
| project srcIP_s, PortCount
| sort by PortCount desc

このクエリがヒットした瞬間、そのIPはブラックリストに入れるべき「不審な客」だ。

—

ステップ2:防御の自動化(Pythonによる自動封鎖)

ログを見てから手動でNSGルールを変更していては遅い。Azure Functionと連携し、異常検知時に自動でNSGの「拒否ルール」を最優先で追加するスクリプトがこれだ。

# Python: Azure SDK for Pythonを使用したNSG拒否ルール追加サンプル
from azure.identity import DefaultAzureCredential
from azure.mgmt.network import NetworkManagementClient

def block_malicious_ip(resource_group, nsg_name, target_ip):
    credential = DefaultAzureCredential()
    network_client = NetworkManagementClient(credential, "YOUR_SUBSCRIPTION_ID")

    # セキュリティルールの定義
    rule_params = {
        "access": "Deny",
        "description": "Auto-blocked by Security Automation",
        "destination_address_prefix": "*",
        "destination_port_range": "*",
        "direction": "Inbound",
        "priority": 100, # 最優先でブロック
        "protocol": "*",
        "source_address_prefix": target_ip,
        "source_port_range": "*"
    }

    # NSGルール更新の実行
    poller = network_client.security_rules.begin_create_or_update(
        resource_group, nsg_name, f"Block_{target_ip.replace('.', '_')}", rule_params
    )
    return poller.result()

# 使用例: ログ分析で検知したIPを即座に遮断
# block_malicious_ip("MyResourceGroup", "MyNSG", "192.0.2.1")

このコードの肝は priority だ。既存の許可ルールよりも先に評価されるよう、低い数値を設定している。これが運用現場における「泥臭いが確実な」防御だ。

—

ステップ3:暗号理論の基本を忘れるな

どれだけネットワークを固めても、アプリケーション層の認証が破られれば終わりだ。特に注意すべきは、公開鍵と共通鍵の「使い分け」だ。

  • 公開鍵暗号(RSA/ECC): 鍵交換に使用する。サーバーの「身元証明(証明書)」と、共通鍵を安全に相手に渡すために使う。
  • 共通鍵暗号(AES-256): 通信の暗号化そのものに使う。計算コストが低く、高速だからだ。

Webアプリ開発において、セッション管理に不十分な暗号化(弱いアルゴリズム)を使っていれば、パケットキャプチャひとつで情報漏洩する。TLS 1.3の利用を強制し、AES-GCMのような認証付き暗号モードを選択すること。

以下は、PHPで安全にデータを暗号化する際の実装例だ。

<?php
// 安全な暗号化の実装例 (AES-256-GCM)
$key = random_bytes(32); // 本来はキー管理サービス(Key Vault等)から取得
$iv = random_bytes(12);  // GCMモードでは12バイトが推奨

$data = "秘密のセッションデータ";
$tag = ""; // 認証タグ

// 暗号化 (openssl_encrypt)
$ciphertext = openssl_encrypt($data, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);

// 送信時は base64_encode(ciphertext . tag . iv) して送るのがセオリー
echo base64_encode($ciphertext . $tag . $iv);
?>

—

まとめ:セキュリティは「継続的な疑い」から生まれる

最後に一つだけ伝えておきたい。セキュリティに「完成」はない。NSGフローログを分析し、異常検知を自動化し、暗号技術を正しく使う。これらはすべて、君たちが自分のシステムを「常に疑い続ける」ための道具に過ぎない。

攻撃者は君たちの隙を狙っている。だからこそ、機械的にログを流すのではなく、「なぜこのアクセスが来たのか?」という問いを常に持ち続けてほしい。それが、君を信頼できるエンジニア、そして真のセキュリティプロフェッショナルへと成長させる唯一の道だ。

次のインシデントが起きる前に、まずは今のNSGのログ設定と、その先にある分析クエリを見直すことから始めてくれ。健闘を祈る。

コメント

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