Azure Firewall の Log Analytics に “SNI TLS extension was missing.” が数百万件単位で記録され、ワークスペース課金の大半を占めてしまう──クラウド移行やゼロトラスト強化の過程でよく起きる悩みです。本記事は、原因の正体を分解しつつ、すぐ効く沈静化策から恒久対策、そしてログコスト最適化までを実運用で役立つ手順としてまとめました。ポータル操作・KQL・DCR 変換の具体例も掲載します。
問題の全体像と最初に押さえるべきポイント
Azure Firewall のアプリケーション ルール ログに、ActionReason_s = "SNI TLS extension was missing." が大量に出力される状況は、概ね次の特徴を持ちます。
- 発生源のログは SourceIP のみが記録され、DestinationIP / FQDN が空(空白)として残る。
- Action は Deny。TLS ハンドシェイク時点で拒否されるため、先のレイヤに進まず宛先情報が決まらない。
- Log Analytics の課金は「取り込み量」に比例するため、膨大なイベントはそのままコスト爆増につながる。
この時点での大原則は、通信そのものは望ましくない(または誤実装)ことが多い一方で、被疑ホストの特定と一時抑制を同時並行で進めることです。抑制だけを先に行うと本質原因が温存され、逆に原因追跡だけを行うとコストが増え続けます。
“SNI TLS extension was missing.” の意味と、なぜ FQDN が空になるのか
SNI(Server Name Indication)は、TLS クライアントが「どのホスト名に対して TLS 接続したいか」をハンドシェイクの ClientHello 拡張で伝える仕組みです。Azure Firewall のアプリケーション ルールで HTTPS を制御する場合、クライアントが送った SNI を見てどの FQDN(ドメイン)へアクセスしようとしているかを判定します。
ところが、クライアントが SNI を送らないと、Firewall は宛先ホスト名を特定できずアプリケーション ルールでの評価が不可能になります。このとき Firewall はハンドシェイク段階で接続を拒否し、ActionReason に “SNI TLS extension was missing.” を記録します。拒否が初期段階で行われるため、DestinationIP や FQDN が未確定のままログに残る、というわけです。 TLS ハンドシェイクと Azure Firewall の評価イメージ
[Client] --ClientHello(SNI=なし)--> [Azure Firewall] --X--> [Internet]
│
└─ Deny(ActionReason: "SNI TLS extension was missing.")
Azure Firewall Standard / Premium いずれの構成でも、アプリケーション ルールで HTTPS をドメイン制御する限り SNI は必須です(Premium で TLS 検査を有効にしていても、初期の可否判断に SNI が関わる点は同様)。
よくある発生原因(現場の実例付き)
| パターン | 何が起きているか | 一次対応 | 恒久対策 |
|---|---|---|---|
| クライアントが IP 直指定で接続 | URL ではなく IP:443 に対して TLS 開始。SNI 付与不可。 | 該当プロセスを特定し一時停止 | FQDN で接続するようアプリを修正 |
| 古いランタイム / TLS ライブラリ | 旧 Java / 旧 .NET / 組み込み TLS スタックが SNI を送らない。 | 一時的に出口を遮断 or 隔離 | ランタイム更新・TLS1.2 以上を強制 |
| プロキシ経由の誤設定 | CONNECT 先を IP 指定、または中間機器が SNI を剥がす。 | プロキシ設定の除外/修正 | プロキシで FQDN 指定を徹底、経路を再設計 |
| スクリプト/検証ツールの誤用 | curl / openssl の試験で IP 直打ちや SNI 未指定。 | 該当端末からのテストを停止 | -servername 付きで再試験、手順を標準化 |
| IoT / 組み込み機器 | ファームが古く SNI 未対応、あるいは送信抑止。 | MAC/シリアルで個体特定し隔離 | ファーム更新 or IPベース許可はネットワークルールへ移管 |
最短で沈静化するための運用テクニック
原因の除去までに時間がかかる場合、ログの嵐を止めつつ、影響範囲を狭めるための実践的な手を打ちます。以下は優先度順の例です。
アプローチ A:上位に「明示的 Deny(ネットワーク ルール)」を置く
- アプリ修正が難しい発信元 SourceIP(またはサブネット)を対象に、443/TCP 宛てを即時拒否するネットワーク ルール コレクションを作成。
- アプリケーション ルールに到達する前に遮断できるため、“SNI missing” ログ自体の発生を抑えられます(残るのは短い NetworkRule の Deny ログ)。
注意:ユーザー影響が大きい可能性があるため、対象範囲は最小限にし、保守時間帯で段階適用を推奨します。
アプローチ B:安全な既知サービスは「サービス タグ + ネットワーク ルールで Allow」
- Windows Update、Azure AD、Storage、Monitor 等、信頼済みの既知サービスはサービス タグを使ってネットワーク ルールで Allow し、アプリケーション ルールの評価をバイパス。
- 結果として SNI なし接続でも拒否されず、ログも激減します。ドメイン単位の制御が弱くなるため、対象サービスは厳選してください。
アプローチ C:Diagnostic Settings の出力先を BLOB Storage に振り分け
- アプリケーション ルール ログを Log Analytics ではなく Storage アカウントにアーカイブ。保管コストが大幅に下がり、必要時のみダウンロード/解析。
- 追跡のしやすさを保つため、イベント ハブや SIEM 側へも同報する二重出力構成が有効です。
アプローチ D:DCR(Data Collection Rule)の変換で“該当イベントだけ”取り込まない
- DCR ベース取り込みを用いると、変換(Transformation)でフィルタして不要イベントをワークスペースに送らない構成が可能です。
- Azure Firewall のアプリケーション ログ ストリームに対し、ActionReason が該当する行を除外するだけで取り込み量が激減します。
発信元の特定と、アプリ側の恒久対策
Log Analytics で「犯人 IP」を炙り出す(KQL)
ワークスペース構成に応じて、旧 AzureDiagnostics とリソース固有テーブル(例:AzureFirewallApplicationRule)のどちらかを使います。
AzureDiagnostics を使う場合
// 直近24時間、"SNI missing" を大量発生させている SourceIP をランキング
AzureDiagnostics
| where Category == "AzureFirewallApplicationRule"
| where Action_s == "Deny"
| where ActionReason_s == "SNI TLS extension was missing."
| summarize events = count() by SourceIp_s
| top 50 by events desc
リソース固有テーブルを使う場合
// 取り込み課金の概算 (_BilledSize はサポートされている場合のみ)
// 単位は MB
AzureFirewallApplicationRule
| where Action == "Deny"
| where ActionReason == "SNI TLS extension was missing."
| summarize events = count(),
approx_ingested_MB = sum(_BilledSize) / 1024.0 / 1024.0
by SourceIp
| top 50 by events desc
ピーク時間帯の推移を把握
AzureFirewallApplicationRule
| where Action == "Deny"
| where ActionReason == "SNI TLS extension was missing."
| summarize events = count() by bin(TimeGenerated, 5m)
| render timechart
SourceIP から実ホストへ辿る(Azure 側)
- VM の NIC 対応表を作り、該当 IP と突き合わせます。例(CLI 一例):
az network nic list --query "[].{nic:name,ip:ipConfigurations[0].privateIPAddress,vm:id}" -o table
- VM の拡張機能や起動ログから、稼働プロセスと実装言語(Java/.NET/その他)を洗い出します。
ホスト上で SNI 未送信プロセスを特定
| OS | 観点 | コマンド例 |
|---|---|---|
| Windows | 443 宛ての外向き接続を PID 単位で確認 | netstat -ano | findstr ":443" Get-NetTCPConnection -RemotePort 443 | Select-Object LocalAddress,RemoteAddress,OwningProcess |
| Linux | TCP セッションのプロセス突き合わせ | ss -ptn '( dport = :443 )' lsof -i :443 |
| パケット観測 | ClientHello に SNI が無いことを検証 | tcpdump -s0 -vvv -i eth0 'tcp port 443 and (tcp[tcpflags] & (tcp-syn) != 0)' |
アプリ側の修正(恒久対策)
- 必ず FQDN で接続する(IP 直指定をやめる)。
- TLS ライブラリ/ランタイムを更新し、TLS1.2 以上を強制。SNI の既定有効化を確認。
- テストには以下を使用(SNI 付き)。
// OpenSSL
openssl s_client -servername example.com -connect example.com:443 -tls1_2
// curl
curl [https://example.com/](https://example.com/) -v
// PowerShell
Invoke-WebRequest [https://example.com/](https://example.com/) -UseBasicParsing
もしプロキシを経由する場合は、CONNECT 先を FQDN:443 で指定し、プロキシ側が SNI を維持する設定であることを確認してください。
ログコスト最適化:取り込み量を“構造化して”減らす
選択肢の比較
| 選択肢 | 効果 | 副作用 / 注意点 | おすすめ度 |
|---|---|---|---|
| Diagnostic Settings を Storage へ | Log Analytics の取り込み課金を直接削減。長期保管も安価。 | 即時検索性は低下。必要時に取り出し・オフライン解析が必要。 | 高(まず検討) |
| DCR 変換で該当行をドロップ | “SNI missing” のみを除外可能。運用要件に柔軟。 | DCR ベース取り込みの設計が前提。誤設定は可観測性低下。 | 高(要変換テスト) |
| アプリケーション ログ全体を無効化 | 即効で取り込み停止。 | 可観測性を大幅に失う。推奨しない。 | 低 |
| ワークスペースのデイリー上限 | 予算の 超過防止 には有効。 | 上限到達後のデータはドロップ。監視空白が発生。 | 中(最後の保険) |
DCR 変換(Transformation)の実装例
プラットフォーム ログを DCR 経由で Log Analytics に送る構成であれば、次のような変換で “SNI missing” を除外できます。
AzureDiagnostics を対象にする場合
// stream: "Microsoft.NetworkAzureFirewallLogs" 等、対象カテゴリに応じて設定
source
| where Category == "AzureFirewallApplicationRule"
| where tostring(ActionReason_s) != "SNI TLS extension was missing."
リソース固有テーブルを対象にする場合
// AzureFirewallApplicationRule 相当のストリームを変換
source
| where tostring(ActionReason) != "SNI TLS extension was missing."
変換は取り込み前に適用されるため課金対象外にできます。導入時は、別ワークスペースへのミラーリングで検証し、誤除外がないかを最低 1 週間は観測すると堅実です。
Diagnostic Settings の設計ポイント
- 出力先は用途に応じて Log Analytics / Storage / Event Hub を併用。
- アプリケーション ルール ログの“アーカイブ”用途には Storage、リアルタイム検知には Event Hub→SIEM を推奨。
- Storage へ送ったログは、ライフサイクル管理(アクセス層の自動移行・期限付き削除)で更にコスト最適化。
ワークスペース側の運用(保管・上限・アラート)
- テーブル別の保持期間を短縮し、価値の高いログだけ長期保持。
- デイリー上限で予算をガード(上限超過はデータ欠損を招くため、根治策と併用)。
- 取り込み量急増を検知する メトリック アラート と、KQL ベースの ログ アラート を二重化。
可視化・検知テンプレート(KQL サンプル集)
発生上位の SourceIP と推移
let since = ago(7d);
AzureFirewallApplicationRule
| where TimeGenerated >= since
| where Action == "Deny" and ActionReason == "SNI TLS extension was missing."
| summarize events=count() by bin(TimeGenerated, 15m), SourceIp
| order by TimeGenerated asc
急増検知(アラート条件にそのまま利用)
AzureFirewallApplicationRule
| where Action == "Deny"
| where ActionReason == "SNI TLS extension was missing."
| summarize events = count() by bin(TimeGenerated, 5m)
| where events > 1000
(旧テーブル)AzureDiagnostics 版
AzureDiagnostics
| where Category == "AzureFirewallApplicationRule"
| where Action_s == "Deny"
| where ActionReason_s == "SNI TLS extension was missing."
| summarize events = count() by bin(TimeGenerated, 15m), SourceIp_s
取り込み量の概算
AzureFirewallApplicationRule
| where Action == "Deny" and ActionReason == "SNI TLS extension was missing."
| summarize approx_ingested_MB = sum(_BilledSize) / 1024.0 / 1024.0
※ _BilledSize の有無は環境によります。列が無い場合は、ワークスペースの「使用状況と推定コスト」から傾向を確認します。
安全性とガバナンスの観点
- ネットワーク ルールで Allow する対象は最小限に。サービス タグの利用と、アウトバウンドの最小権限設計を徹底します。
- “SNI missing” を無条件に通してしまうと、ドメイン ベース制御が効かない経路が恒久的に生まれます。恒久対策は必ずアプリ側で実施。
- ログ抑制は「可観測性とのトレードオフ」。DCR 変換で精密に除外し、監査用には Storage に保存する二段構えを推奨します。
現場で使える「段階的な収束プラン」
- 0日目:上位 SourceIP を特定。発信元サブネットと担当者を割り当て。
- 1日目:該当 SourceIP(/ サブネット)に対し、ネットワーク ルールの Deny を暫定適用しログ雪崩を止める。並行して Diagnostic Settings を Storage 併用に変更。
- 2〜3日目:DCR 変換を検証環境で適用し、SNI missing を除外。新旧ワークスペースを比較して誤除外が無いか確認。
- 1週間以内:アプリ/クライアント更新を展開。IP 直指定を排除、TLS1.2+ を強制。プロキシ設定を是正。
- 2週間以内:ネットワーク ルールの暫定 Deny を段階解除。Workbook とアラートで 再発監視を定着させる。
検証のための具体的コマンド例
| 目的 | コマンド例 | 期待結果 |
|---|---|---|
| SNI 付き接続の確認 | openssl s_client -servername contoso.com -connect contoso.com:443 -tls1_2 | ハンドシェイク成功。Firewall ログで Deny が発生しない。 |
| SNI なし接続の再現 | openssl s_client -connect 93.184.216.34:443 -tls1_2 | Firewall 側で “SNI missing” を記録し Deny。 |
| curl の SNI(既定で有効) | curl -v https://contoso.com/ | HTTP 200/3xx。SNI が送られる。 |
| PowerShell による疎通 | Test-NetConnection -ComputerName contoso.com -Port 443 | 到達性と遅延を確認。アプリ側問題の切り分けに有効。 |
設計リファレンス:ルール配置の考え方
ルール評価順は NAT → ネットワーク ルール → アプリケーション ルール の順で行われます。したがって、ネットワーク ルールで先に Allow/Deny することで、アプリケーション ルール(SNI 依存)での評価を回避できます。実運用では以下のように整理するとわかりやすくなります。
| ルール コレクション | 用途 | 例 |
|---|---|---|
| RCG-00-Network-Allow-Trusted | 安全な既知サービスをバイパス | Service Tag: AzureActiveDirectory, AzureMonitor, Storage |
| RCG-01-Network-Deny-Temporary | SNI missing 発生源の暫定遮断 | Source: 10.0.1.0/24 → Any:443/TCP Deny |
| RCG-10-Application-Policy | 通常のドメイン単位の制御 | FQDN ベースの Allow/Deny |
トラブルの再発を防ぐ運用チェックリスト
- 新規アプリ導入時に IP 直指定や独自 TLS 実装が無いかセキュリティレビューで確認する。
- プロキシ設定は FQDN 指定+除外リストを標準化、検証手順をドキュメント化。
- ランタイムと TLS ライブラリの 更新方針(EOL 管理)を明確化する。
- Azure Firewall のログ設計は 「観測の粒度」×「コスト」×「保存期間」のバランスで定期点検。
- ワークスペースの 取り込み増加アラートをしきい値管理し、ナレッジ化(過去事例と対応手順の紐づけ)。
よくある質問(FAQ)
Q. TLS1.3 や HTTP/2/3(QUIC)でも SNI は必要ですか?
はい。SNI は TLS の拡張として扱われ、HTTPS の仮想ホスティングに広く使われます。HTTP/3(QUIC)でも、接続先の判定にホスト名情報が必要であり、結局はホスト名を提示できないトラフィックはドメイン ベース制御と相性が悪いままです。
Q. “SNI missing” を許可するルールを作ってもいいですか?
原則おすすめしません。ドメイン単位のセキュリティ境界が崩れます。どうしても必要なら、特定の SourceIP から特定のサービス タグ/アドレス範囲へ限定したネットワーク ルールで Allow し、監査ログとアラートを強化してください。
Q. DestinationIP が空だと、どこに繋ぎにいっているか絶対に分かりませんか?
Firewall のそのログだけでは不十分ですが、ホスト側の接続履歴・パケットキャプチャや、プロキシ/ロードバランサのログと突き合わせることで推定できます。Azure の資産管理(NIC と VM の突合)やプロセス監視も併用しましょう。
まとめ:コストを抑えつつ、根本原因をなくす
“SNI TLS extension was missing.” は、クライアントが SNI を送っていないというシンプルな事実を示すアラートです。しかし実運用では、IP 直指定・古いランタイム・プロキシ誤設定・組み込み機器など、背景の事情は様々です。最短ではネットワーク ルールや出力先変更でログの雪崩を止め、並行してアプリ側を是正し、DCR 変換で不要ログを取り込まない──この三本柱で進めるのが最も効果的です。
本記事の手順(KQL・DCR・ルール設計)を適用すれば、原因の特定→暫定抑制→恒久対策→コスト最適化のサイクルを短期間で回せます。Log Analytics の課金を抑えつつ、セキュアで予測可能な出口制御を実現しましょう。
付録:そのまま使える運用スニペット集
Workbook:日別の“該当イベント総量”
AzureFirewallApplicationRule
| where Action == "Deny" and ActionReason == "SNI TLS extension was missing."
| summarize events = count(), approx_MB = sum(_BilledSize)/1024.0/1024.0 by bin(TimeGenerated, 1d)
| project 日付=format_datetime(TimeGenerated, 'yyyy-MM-dd'), 件数=events, 取り込みMB=approx_MB
ログ アラート用(急増しきい値)
AzureDiagnostics
| where Category == "AzureFirewallApplicationRule"
| where Action_s == "Deny" and ActionReason_s == "SNI TLS extension was missing."
| summarize events=count() by bin(TimeGenerated, 5m)
| where events > 500
プロキシ経由時のチェックリスト
- ブラウザ/エージェントのプロキシ設定は FQDN:443 指定になっているか。
- プロキシの CONNECT ログに、ホスト名が記録されているか。
- SSL/TLS 中継機器が SNI を透過する設定か(中間復号を行う場合は CA 配布/例外設定を確認)。
ネットワーク ルールでの暫定抑止テンプレート
// 例:RCG-01-Network-Deny-Temporary
// Source: 10.0.1.0/24, Protocol: TCP, Destination: Any, Port: 443, Action: Deny
// 優先度はアプリケーション ルールより前(小さい数字)に配置
サービス タグを用いたバイパス例
// 例:RCG-00-Network-Allow-Trusted
// Destination: Service Tag = AzureActiveDirectory, AzureMonitor, Storage
// Protocol: TCP, Port: 443, Action: Allow

コメント