【考察】DNSパケットをNTPパケットとして誤解釈させる手法

パケットを誤解釈する実装があった時、違うプロトコルのパケットに対して応答してしまうのではないのか。そんなことが実際にあるのか考察してみる。

今回は、DNSサーバとNTPサーバで、そのような通信を意図的に引き起こせるかのか検討していく。

先に結論を書いておくと、その様なパケットを構成することは出来そう。

目次

誤解釈をするシチュエーション

この様な思考実験をするにあたって、それを考えるきっかけを補足する。

NTPサーバに対してDNSのパケットが届くというシチュエーションがあるのか?誤解釈をしてしまって問題になるのか?という疑問は当たり前にある。その、一つの例として反射攻撃が思いつく。。

反射攻撃(アンプ攻撃)は、攻撃者が送信元IPアドレスを偽装してパケットを送信することで、応答がVictim宛に届くという手法である。
(レスポンスサイズ>リクエストサイズ という通信特性を応用して大量のトラフィックを生み出す)

反射攻撃は通常であれば、図①、②で終わりますが さらに③の応答パケットを生み出すことができてしまう。

なお、ネタバレをすると今回の構成は、反射攻撃として手法として有用かというとそんなことはないように思う。

(反射攻撃に対してさまざま防御手段があります。偽装したIPアドレスは不正な経路から届くためブロックしたり、Victim側ではウェルノーンポートが送信元IPになるためそれでブロックするなど防御手法があります。)今回は、送信元IPを偽装するところは本論ではないので、実際に試したりはしません。

実験構成

DNSパケットをNTPパケットとして誤解釈する事を実証する。

ローカルにBindサーバ・ntpdサーバを起動し、①実験者から正規のDNSパケットを送信する。②応答としてDNSパケットが得られる、それを③NTPサーバにそのまま送信する。中継しているが、正規のDNSパケットがNTPサーバで誤解釈され④NTPレスポンスが発生することが確認できる。

  • bind9 1:9.18.39-0ubuntu0.24.04.7
  • ntp 1:4.2.8p15+dfsg-1ubuntu2

今回は簡易的な実験として、bindサーバには細工したレコードをプリセットしている。

おさらい

DNSレスポンスと、NTPリクエストのパケットの構造をおさらいする。(なお、構成図②に相当する、同じバイナリからなるパケットにて例示している)。

まずはDNSレスポンスから。

下記などからなる

  • Transaction ID
  • Flags
  • Queries
  • Answer

NTPリクエスト

下記などからなる

  • Flags
  • 時刻用パラメータ
  • 時刻情報
  • オプショナルである拡張領域 (length+valueからなる)

パケットの細工

NTPリクエストとして受理されるためには次の条件を満たさなければならない。

  • NTPバージョンが正しい (1~4)
  • Modeがクライアントモード
  • パケット長が整合する
    • NTPパケットとして固定長の48バイト
    • 拡張フィールドのLenght および 実際のバリューの長さ

実験者は、①DNSリクエストを工夫することで、これらが守られるようなDNSレスポンスを意図的に作ることを目指す。

  • NTPパケットとしての冒頭のバージョンやModeは、①で送るDNSリクエストのTransaction IDを任意に設定することでコントロールできる。
  • 拡張フィールドに含まれるLength及び、パケット長が整合することは運ゲーである。が、DNSリクエストでRDフラグを有効にしつつ、任意のTXTレコード(TXTレコードはバイナリ値も許可されている)を引かせることで、多少生成するDNSレスポンスのコントロールすることが出来る。今回はシンプルにAAAAレコードのIPアドレス部分でLength長を調整している。

↓NTPリクエストとしての拡張フィールドにおいて、このLengthとパケット長が整合しないとエラー扱いになる。

(実際のDNSサーバでは長さが変わる要因が複数あるが、今回は実証としての一例まで。)

実際行われた通信

構成図で示した通りの通信が、実際に成功している例

[再掲] 構成図。

(④で返されるNTPレスポンスとしては小さいパケットになるので、アンプ攻撃で有用とは思えない。あくまで思考実験として試したというぐらいの結果)

サーバ実装上の問題点

今回は、実際にはntpdが不正なパケットを受理してしまっているのが一つの要因としてある。

一部のパラメータは、仕様上の不正な値になってしまっている。これらを不受理にスべきである。

サーバを実装する人は不正なパラメータをエラーにスべきです。

終わりに

今回はDNSパケットとNTPパケットの、誤解釈をテーマに扱ったが、UDPプロトコル同士であれば発生しうる。

プロトコルを設計するうえで、このような誤解釈を本質に防ごうと思ったら、DTLSやQUICを用いることで暗号処理として誤解釈を防げるので、それが良さそうではある。

宇宙用 IPアドレスレンジを割り当てる議論

IETFでは宇宙でのIP通信の利用について議論が行われています。

IPv6 Address Space for Space

その中で、宇宙空間用のIPv6アドレスの割り当てについて『IPv6 Address Space for Space』というIndividual Draftが提出されています。

現在、IPアドレスは番号資源として管理され、地域ごとに5つのRegional Internet Registry(RIR)が分配・登録を担っています。地球周回衛星通信で使われるIPアドレスも、通常は運用組織の事業地域に対応するRIRから取得されます。

一方で月・火星などに複数組織のネットワークが増えると、同じ天体上に地上の所属地域ごとに異なるプレフィックスが混在し得ます。本draftは、天体とその近傍に対応する上位プレフィックスを用意することで、将来のルーティングテーブルにおけるプレフィックス集約と運用効率を確保しようとする提案です。

ただし、そのレンジの割り当ての運用については特に規定していません。(もともとは宇宙用RIRを作る議論もあったんですが、Draftとしてはこの形落ち着いたようです)。

IANA Considerations

DraftのIANA Considaretoinsには次の通り書かれています

IANAは、宇宙環境での使用を目的としたアドレス空間の割り当てを求められています。IANAは、以下の天体に対してプレフィックスを割り当てるよう求められています

  • 月とその周辺
  • 地球のラグランジュ点
  • 火星

宇宙通信が上記以外の天体にも拡大される場合、IANAは追加の割り当てを行うべきである

NROは、本書に概説されている集約原則に従って、これらのブロックからアドレスをどのように割り当てるかを決定する

おわりに

まだ提案でありIETFのTIPTOP WG内でもDraftの中でどこまで規定するのか意見は収束しておりません。まだ議論が続くものとおもいます。

そのごもし進むのであれば、ICANN・NROなどとともに割り当てや運用について議論されていくものと思われます。

新しいHTTPステータスコード 419 Purpose Declined

HTTPに新しいステータスコードを定義する『The Purpose Declined HTTP Status Code』が提案されています。

現在 IETF HTTP ワーキンググループの WG draftとなっています。

419 Purpose Declined

この提案の背景としてあるのが、ブラウザが送信する preload/prefetch リクエスト といった、投機的リクエストに対する拒否を示すステータスコードが欲しいということでした。

サーバ側は、投機なリクエストに対して、実際に必要になった時に最新のリソースを再取得して欲しいと伝える手段が今までなく、4xxや5xxを返していましたがブラウザからするとただのエラーに見えていました。(もちろんサーバを監視する側としても、4xxや5xxが出るのも紛らわしいです。)

もともとは、『4xx Preliminary Request Denied』とする議論もありましたが、Sec-Purposeヘッダに対する拒否として現在の Purpose Declined に変更されました。

Draft 01 の更新点

今回でた draft-01 では、ステータスコードにまつわる動作が追記されました

  • レスポンスのボディ(Content)は長さ0であること
  • CDNやプロキシがこのステータスコードを返しても良い
  • このステータスコードのレスポンスをキャッシュすべきではないこと

などです

HTTP ステータスコード 402 Payment Required の行方

HTTPのステータスコードで「402 Payment Required」はRFC 9110で、将来のために予約されています。

The 402 (Payment Required) status code is reserved for future use.

402の意味も、具体的なサーバやユーザエージェント側の動作も定義されていないのが現状です。

Webサーバは任意のステータスコードを返すことは容易であるため、ある実装がステータス402を返すことも簡単です。ただし、相互運用性の観点からは、まだブラウザやプロキシ(ロードバランサ・キャッシュサーバ)が期待通り動作するとは言えません。

そのような中でステータス402を利用する提案仕様が幾つか出ているのが現状です。

402の動作を規定する様々な提案

最近話題のx402を始めとして、ステータス402を利用する動きは多くあります。ユースケースとしては、API呼び出し1回ごとの課金、記事・コンテンツの少額課金、AIエージェントによる有料API利用、クローラへの課金などです

ただし、402を受け取ったクライアントが何をすべきかは未解決です。

IETF側での議論

IETF HTTP WGのメーリングリストで、「IETF外でステータスコードのセマンティクスが定義されることについて」、意見が投げかけられた。これは最初の投げかけであり、他の人がどう考えているか伺うものであり、具体的な対応案は議論の最中である。

今のところ、IETFで402のセマンティクスを標準化する価値がある、という意見が出ています。また、暗号資産決済だけでなく、購読資格や、匿名性を保った支払い済み・利用資格の証明など、幅広いユースケースを想定し得る点も議論されています。

たとえば、mnot氏は 「衝突や不適切な慣行を避けるため」とし、現在のアイディアをdraftとして共有しています。

The 402 (Payment Required) HTTP Status Code

mnot氏の提出した『The 402 (Payment Required) HTTP Status Code』では、402ステータスコードのセマンティクスと一般的な動作を規定しています

  • 402 (Payment Required) は、リソースの要求に対して支払いが必要な事を示します
  • 402レスポンスを生成するサーバは、クライアントがリクエストを再試行できるように、支払い方法を示すべきです
  • 中継サーバ(intermediary)も、転送に支払いが必要な場合にこのステータスコードを利用できる
  • 402 (Payment Required) レスポンスはヒューリスティックにキャッシュできない

mnot氏の草案は、特定の暗号資産や決済方式をHTTP標準に取り込むものではありません。具体的な支払い方法を示すレスポンスヘッダは定義していません。まずは「支払いが必要である」ことをHTTP上で一貫して表現するための、最小限の意味論を定めようとするものです。

HTTP ステータスコード 402 Payment Required の行方

これからの動向が気になるところ...

引き続き IETF HTTP WGのメーリングリストで議論されると思います
https://lists.w3.org/Archives/Public/ietf-http-wg/

次世代の動画配信仕様 Media over QUICをCloudflareで試す

IETFでは新しいリアルタイム動画配信仕様である『Media Over QUIC』の標準化を進めています。

この仕様では、CDNなどを介し大規模にリアルタイム配信できるようになっています (素のHTTPではないので、CDN側の対応は必須)。

WebTransportで動作するため、視聴者側はWebブラウザでも動作します。例えば下記サイトで実験できなDemoを視聴できます。
▶ Watch - Media over QUIC

CloudflareのMedia over QUIC

Cloudflareでは、Media over QUIC のリレーサーバを作成できます (現在Beta版)。
▶ Overview · Cloudflare MoQ docs

リレーサーバの作成

Cloudflare ダッシュボードより

  • メディア > Realtime > MoQ Relay

[リレーを作成] で作成できます。このとき、PublisherとSubscriber用のアクセストークンが表示されるのでメモして取っておきましょう

動作確認する

cloudflareが作っているmoc-rsを使います
github.com

cargoおよびffmpegが必要ですので事前にインストールしておきましょう。

準備 (みんな大好きBigBuckBunnの動画を配置します)

$ git clone https://github.com/cloudflare/moq-rs.git
$ cd ./moq-rs
moq-rs$ wget https://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4.zip
moq-rs$ unzip BigBuckBunny_320x180.mp4.zip
moq-rs$ mv ./BigBuckBunny_320x180.mp4 ./dev/bbb.mp4

./dev に各種便利スクリプトが配置されているので、トークン付き URL を渡してやればうまくいきます

Publish

moq-rs$ URL="https://draft-16.cloudflare.mediaoverquic.com/<*TOKEN> " NAME=bbb ./dev/pub

Subscribe

moq-rs$ URL="https://draft-16.cloudflare.mediaoverquic.com/<*TOKEN>" NAME=bbb ./dev/sub 

うまくいくと動画が再生されます

おわり

CloudflareはMedia over QUICの仕様のうちDraft 16をサポートしてます。他の実装により対応しているdraft バージョンが異なるため、ご注意ください。

最新仕様が気になる人は下記を参照
www.ietf.org

ということで、これで遊ぶぞー

WebサイトのCookieの受け入れ方針を伝える『Cookie-Preference』ヘッダ

IETFに「The Cookie-Preference HTTP Header Field」という提案仕様が提出されています。

Webサイトを閲覧していると、Cookieについて受け入れる/拒否するのボタンを選択させられる機会もあるかとおもいます。それに対して、HTTPリクエストで、WebサイトにCookieの受け入れ方針を伝えられるようにする提案です。

例

Cookie-Preference: essential

Cookie-Preference: all; level="high"

Cookie-Preference: ask; granularity="category"

Cookie-Preferenceヘッダの値は次の値が定義されています

  • all: すべてのクッキーを受け入れる
  • essential: 必要不可欠なクッキー(セッション、認証、セキュリティなど)のみを受け入れる
  • none: すべてのクッキーを拒否する
  • ask: 必須ではないクッキーを設定する前に、明示的または詳細な同意を求める

その他にも、ユースケースとしてlevelやgranularityをパラメータとして例示していますが、仕様中では厳密な定義を与えてはおりません。

おまけ (その他の類似機能)

古くは、DNTヘッダー (Do not track)や、Sec-GPC(Global Privacy Control)ヘッダというものもあるが Cookieの受け入れ条件を指定出来ないというのが主な違いになります

Webクローラーが付加するWeb-Bot-Authの署名を検証してみる

GoogleのクローラーBotがWeb-Bot-Authの仕様を実験実装してたりします。
developers.google.com

Web-Bot-Authでは、Botはリクエストに署名を付けHTTPリクエストを送信してきます。こうすることで、User-Agentだけに頼るのではなく、署名検証を通してそのBotが正規のものか確認できます。これにより、AIの学習をしたりするBotを適切に認証したり、はたまたBotの学習行為に対して課金したりできます。

実際に試す

私の個人サイトへ、Google Botが署名付きでリクエストを送ってきてくれれば良いものの、、、まだ付加してこなさそうです。
アクセスログを見ると、ahrefs.com というBotがWeb-Bot-Auth署名付きのリクエストを送ってきたので、署名検証をしてみます。

署名検証に必要なパラメータ

署名の検証に必要な情報は大体次のとおりです。
基本的なパラメータは『RFC 9421 - HTTP Message Signatures』に則ったパラメータがHTTPリクエストにのかってきます。
(下記jsonは便宜上、json形式で表現しただけで。仕様で定義されているフォーマットでは有りません)

{
  "received_at_unix": 1781486729,
  "authority": "asnokaze.com",
  "headers": {
    "signature": "sig=:+wkpd7nQVTXX2qrvPUiDm2ZXU5Uzdgi759hZgzZmgViGue+zgxa12+ysInY1IBEIjqFUpgG8YzJ8FekBZ6JGAw==:",
    "signature-input": "sig=(\"@authority\" \"signature-agent\");created=1781486729;keyid=\"e3vpiy0B6M1Wdxnizw3dqRSgpqS6SXM2qiQ6HtUwZ5g\";alg=\"ed25519\";expires=1781490329;nonce=\"NcuWdzP41zzBqJr0SAr4RJQQZaKt72aCN8rk_Ee7z_vgaeD-4l7G0PMisYEZnRftOxmoNoFnM-fiJzNaea2ZLg\";tag=\"web-bot-auth\"",
    "signature-agent": "\"https://ahrefs.com\""
  }
}
検証

主な流れは次の通り

  • signature-agentを読み取り、https://ahrefs.com/.well-known/http-message-signatures-directory にアクセスし、keyidに対応する公開鍵を取得する
  • 取得した公開鍵と、各種パラメータ(暗号アルゴリズム, nonce, tag)より、署名値を検証する (署名の範囲は authorityヘッダとsignature-agentヘッダ)
  • 時刻が expires を越えてないことを確認する

これにより、アクセスしたBotがahrefsの正規のBotであることが確認できる

おまけ: 実験コード

上記のような、署名検証に必要な情報をまとめたjsonで、Web-Bot-Authの署名検証をする実験コード
https://gist.github.com/flano-yuki/de66a66fbb64340658393960a566090d