サマリ
DNSリバインディングが最近注目されている。Google Chromeは最近になってローカルネットワークへのアクセス制限機能を追加しており、その目的の一つがDNSリバインディング対策になっている。Googleが提供するWiFiルータGoogle Nest WiFiはデフォルトでDNSリバインディング対策機能が有効になっている。 DNSリバインディング対策は、攻撃対象アプリケーションで行うべきものであるが、ブラウザ、PROXYサーバー、リゾルバ等でも保護機能が組み込まれている。本稿ではそれら対策機能の状況と対策の考え方について説明する。
DNSリバインディング(DNS Rebinding)とは
DNSリバインディングはDNS問い合わせの時間差を利用した攻撃です。DNSのTTL(キャッシュ有効期間)を極めて短くした上で、1回目と2回目の問い合わせ結果を変えることにより、IPアドレスのチェック等を回避する攻撃です。下図は、DNSリバインディング時のDNS問い合わせの様子です。
DNSリバインディングの主な脅威
DNSリバインディングによる脅威は、DNS問い合わせがあるところ全てにありえますが、典型的な脅威として下記があります。
- 外部からアクセスできない対象へのブラウザ経由での攻撃
- SSRF攻撃のチェック回避
ブラウザ経由での攻撃
ブラウザ経由での攻撃の概要図を示します。
上図は、攻撃者の誘導により、被害者が罠のページ(http://trap.example.org/)を閲覧しているところです。罠ページが閲覧された瞬間にDNSのAレコード(IPv4アドレス)を変更し、罠ページは10秒後にXMLHttpRequestにより http://trap.example.org/secret.html をアクセスします。この時点ではIPアドレスは変更されているためイントラネット内のサーバーにアクセスします。この攻撃により、外部からは直接アクセスできないサーバーにアクセスできます。
このように、DNSリバインディングは、イントラネット内のサーバーの他、ルーターやファイアウォール、IoT機器など、利用者のパソコン自身など、「外部からはアクセスできないが内部ネットワークからはアクセスできる」機器やサーバーが主な攻撃対象です。IPアドレスのみでアクセス制御されているサイトも攻撃対象になります。
攻撃対象の例として、Ruby on Railsの開発環境がありました。Railsの開発支援機能web-consoleはDNSリバインディングに脆弱で、任意のコードを外部から実行できる問題が指摘されていました。
DNS rebinding attacks protection in Rails 6
このため、バージョン6以降で開発版でのDNSリバインディング対策が入るようになりました)。具体的にはHostヘッダのチェックであり、これはDNSリバインディングの定石的な対策方法の一つです。
- Rails 6 adds guard against DNS rebinding attacks | Saeloun Blog
- Rails 6にDNSリバインディング攻撃防止機能が追加された(翻訳)|TechRacho by BPS株式会社
- Guard against DNS rebinding attacks by permitting hosts by gsamokovarov · Pull Request #33145 · rails/rails
この機能は開発環境のみでプロダクション環境では無効になります。 これは、Web-consoleのようなデバッグ支援環境でリモートコード実行の脆弱性が入りやすい(現実にあった)ので、開発環境だとDNSリバインディングの脅威があるという想定だと思います。
【参考】
SSRF攻撃との組み合わせ
SSRF攻撃の際に使われるDNSリバインディングについては以下の記事をお読みください。
EC2上でDNS RebindingによるSSRF攻撃可能性を検証した | 徳丸浩の日記
ここでは概要を説明します。以下のようなスクリプトがEC2上で動いているとします。外部からURLを取得して、その内容を表示するものです。
$url = $_GET['url'];
$urlinfo = parse_url($url);
$host = $urlinfo['host']; // URLからホスト名を取り出し
$ip = gethostbyname($host); // 接続先IPアドレスを取得
if ($ip == "169.254.169.254") { // AWSのIMDSチェック
die("Invalid host $host"); // IPアドレスが169.254.169.254ならエラー
}
$ch = curl_init($url); // URLからコンテンツ取得、表示
上記のスクリプトに、IPアドレス 169.254.169.254 のチェックをしていますが、これはEC2のIMDS (Instance Metadata Service)という機能を悪用されることを防ぐためです。IMDSは下記のように、169.254.169.254という仮想的なエンドポイントにアクセスすると、参照元インスタンスの設定を返します。以下は、EC2インスタンスに付与されたIAMクレデンシャルを参照する様子です。
[ec2-user@web ~]$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/XXXX
{
"Code" : "Success",
"LastUpdated" : "2022-05-08T04:17:09Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIAR6Mxxxxxxxxxxxxx",
"SecretAccessKey" : "Wrt7en1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Token" : "IQoJb3JpZ2luX2VjEAQaDxxxxxxxxxxxxxxxx...",
"Expiration" : "2022-05-08T10:52:42Z"
}
先に示したスクリプトは複数の抜け道があるのですが、その一つがDNSリバインディングによる攻撃です。以下は、DNSサーバーにdigコマンドにより連続してアクセスしたところ、2回目にIMDSのIPアドレスが返っている様子です。
[ec2-user@web ~]$ dig example.net +short ; dig example.net +short
203.0.113.2
169.254.169.254
[ec2-user@web ~]$
これにより、IPアドレスチェックの際は無害な内容を返し、本番アクセスの際はIMDSのIPアドレスということになって、SSRF攻撃によりIMDSの内容を盗むことができます。
【参考】
DNSリバインディングの対策
下図はDNSリバインディングの対策ができる場所を示しています。
以下、図中の数字の順に説明します。
① 対象サイト側の対策
DNSリバインディングの根本対策は、攻撃対象のサイトや機器側で行われるべきです。DNSリバインディングの対策は以下のいずれかにより対策できます。
- ホスト名のチェック、あるいはダミーのデフォルトバーチャルホスト
- 認証機能の実装(安全なパスワードを設定すること)
以下はNginxでダミーのバーチャルホストを設定している例です。本番サイトはexample.jpですが、それ以外のホスト名のリクエストはすべてダミーのコンテンツが返されることになります。ダミー側には機密情報や機能などはないので、DNSリバインディング攻撃を「受け流す」ことができます。
# ダミーのバーチャルホスト(Hostヘッダがexample.jp以外はこちらが設定される)
server {
listen 80 default_server;
server_name '_';
root /var/www/dummy; # 無害なコンテンツが返される
}
# 本番サイトのバーチャルホスト
server {
listen 80;
server_name example.jp;
…
あるいは、認証機能の実装でも対策になります。DNSリバインディング攻撃は利用者のセッションを乗っ取れるものではないからです。IoT機器やルーターなど初期パスワードが設定されているものは、必ず初期パスワードを変更する必要があります。
【参考】
SSRF対策のパイパスについては、SSRF自体の対策の中で考える必要があります。詳しくは以下のコンテンツを参照ください。
② ブラウザ側対策
DNS Pinning
主要ブラウザでは、DNSリバインディング対策としてDNS Pinning(DNSピニング)が実装されています。DNS Pinningとは、DNSのTTLが短い場合でも、DNSの参照結果を一定時間保持することです。下図は主要ブラウザのDNS Pinningの期間です。
Google Chrome | Firefox | Safari | IE |
---|---|---|---|
1分程度 | 1秒~70秒 | 15秒~30秒 | 無期限? |
DNSリバインディング対策という観点からはDNS Pinningの期間は長いほど安全ですが、DNSリバインディング以外の正当な状況でサーバーのIPアドレスが変化する場合もあるので、過度にDNS Pinningの期間が長いと副作用もあると考えられます。DNS Pinningの期間は時代とともに変化しており、最近はウェブサイトの本来のIPアドレスが短期間で変化するため、DNS Pinningの期間は短めにされる傾向があります。
Google Chromeで提唱されている対策
Google ChromeではDNS Pinning以外に「ローカルネットワークに対する攻撃」への防御機能が実装され始めています。Google Chrome 94では、XMLHttpRequestやFetch APIにより「パブリックネットワークからプライベートネットワークへの送信」については、HTTPSであることを要求するようになりました。
Starting in Chrome 94, public non-secure contexts (broadly, websites that are not delivered over HTTPS or from a private IP address) are forbidden from making requests to the private network.
Private Network Access update: Introducing a deprecation trial - Chrome Developers より引用
Chrome 94以降、パブリックな非セキュアコンテキスト(広義には、HTTPSで配信されていないウェブサイトや、プライベートIPアドレスからでないウェブサイト)は、プライベートネットワークへのリクエストが禁止されるようになりました(私訳)。
これにより、Google ChromeでDNSリバインディング攻撃しようとすると以下のようなエラーになります(ホスト名は例示用のもの)。
Access to XMLHttpRequest at 'http://example.com/secret.txt' from origin 'http://example.org' has been blocked by CORS policy: The request client is not a secure context and the resource is in more-private address space `private`.
エラーメッセージとしてはCORSエラーとなっていますが、DNSリバインディングは通常同一オリジンポリシーの範囲内のアクセスであり、当然CORSのエラーではありません。しかし、Google Chromeの最近のセキュリティ機能では、パブリックネットワークからプライベートネットワークへのリクエスト、およびプライベートネットワークからローカルホストへのアクセスについては、通常より厳しい制限を課していることになります。
このセキュリティ機能は、ローカルネットワークに対するCSRF緩和策として導入されたようですが、DNSリバインディング対策としても機能すると思われます。なぜなら、HTTPSによりDNSリバインディング攻撃しようとすると、攻撃先サーバーへのアクセスで証明書のエラーエラーになり、アクセスは停止するからです。
さらに、Google Chrome 102 以降では、このような状況でプリフライトリクエストが送信されるようになる予定です。
Preflight requests for PNA are also sent for same-origin requests, if the target IP address is more private than the initiator. This is unlike regular CORS, where preflight requests are only for cross-origin requests. Preflight requests for same-origin requests guard against DNS rebinding attacks.
Private Network Access: introducing preflights - Chrome Developers より引用
PNA(Private Network Access)のプリフライトリクエストは、ターゲットIPアドレスがイニシエーターよりもプライベートである場合、同一オリジンのHTTPリクエストに対しても送信されます。これは、プリフライトリクエストがクロスオリジンリクエストに対してのみ送信される通常のCORSとは異なります。同一生成元リクエストのプリフライトリクエストは、DNSリバインディング攻撃を防止します(私訳)。
引用元にもDNSリバインディング攻撃防止のためと明記されています。
このようにGoogle Chrome(あるいはChromium系ブラウザ)を使うことでDNSリバインディングの対策が強化されますが、これら対策はProxyサーバー経由での通信には適用されません。Proxy経由での通信の場合、ブラウザからはパブリックあるいはプライベートという区別がつかないからです。これはDNS Pinningについても同様であり、各ブラウザ共通の挙動になります。
③ Proxyサーバーでの対策
前述のようにProxy経由でのブラウザ利用の際には、ブラウザ側のDNSリバインディング対策は機能しないため、Proxy側での対策が求められます。よく理由されるFoward ProxyであるSquidには、以下のDNS Rebindin対策が利用できます。一方、Apache HTTPdをProxyとして使う場合、DNSリバインディングに使える機能は調査の範囲では見つかっていません。ご存じの方はぜひご指摘ください。
(1) IPアドレスによるアクセス制御
Squidのアクセス制御機能から、接続先(Destination)のIPアドレスを制限することができます。以下は、192.168.0.0/24に対するアクセスを拒否する例です。
acl localnet dst 192.168.0.0/24
http_access deny localnet
(2) DNS Pinning
SquidはDNSキャッシュの上下限を指定することができます。
ディレクティブ | 意味 | デフォルト値 |
---|---|---|
positive_dns_ttl | DNSキャッシュの上限 | 6時間 |
negative_dns_ttl | DNSキャッシュの下限 | 1分 |
これらのうち、negative_dns_ttl はDNS Pinningの用途に使うことができます。 しかしながら、negative_dns_ttlをむやみに長くすると副作用もあります。もしも正規のDNSクエリに失敗した場合、その失敗の結果もキャッシュされるからです。また、前述のように、正常なIPアドレスが短期間で変わる場合もありえます。このため、negative_dns_ttlをデフォルトより長くすることはお勧めできません。
結論としては、SquidでDNSリバインディング対策する場合は、(1)のアクセス制御による方法がお勧めです。
【参考】
④ DNSキャッシュサーバーでの対策
リゾルバ(DNSキャッシュサーバー)による対策も可能です。Squidの項で説明したDNS Pinningとアクセス制御の両方が主要リゾルバで用意されています。
DNS Pinning
下表に主要ブラウザでDNS Pinningを指定するディレクティブを示します。DNSコンテンツサーバー側でTTL=0秒などと短い秒数が指定されていた場合でも、こちらで指定した秒数に切り上げられます。
リゾルバ | bind9 | unbound | dnsmasq | PowerDNS | KNOT DNS |
---|---|---|---|---|---|
設定項目 | min-cache-ttl | cache-min-ttl | min-cache-ttl | minimum-ttl-override | cache.min_ttl() |
備考 | 90秒以下 | 制限など記載なし | 1時間以下 | デフォルト1秒 | デフォルト5秒 |
PowerDNSとKNOT DNSは最短のTTLがデフォルトとして指定(1秒~5秒)されており、TTL=0は許容されない設定になっています。このような控え目の設定でもSSRF攻撃の緩和には役立つと思われます。
特定IPアドレスの拒否
また、多くのDNSサーバーは、DNSリバインディング対策として、IPアドレスのフィルタリング機能を提供しています。下表はIPアドレスの拒否リストあるいはDNSリバインディング対策として使えるプライベートIPアドレス等を拒否するためのディレクティブの一覧です。
リゾルバ | bind9 | unbound | dnsmasq | PowerDNS | KNOT DNS |
---|---|---|---|---|---|
設定項目 | deny-answer-addresses | private-address | stop-dns-rebind | 設定は見つからなかった | modules.load('rebinding < iterate') |
備考 | サブネットマスク等で指定 | サブネットマスク等で指定 | 一括停止 | Luaスクリプトで可能 | 一括停止、詳細にやるならLuaスクリプト |
私の調査では、PowerDNSのみ該当する項目を探せなかったのですが、Luaスクリプトで特定IPアドレスを拒否することは可能でした。 また、dnsmasqとKNOT DNSはIPアドレス指定ではなく、DNSリバインディングに使われそうなサブネットマスクを一括して拒否する形になっています。
また、単体のリゾルバではありませんが、Googleが提供するGoogle Nest WiFiにはDNSリバインディング防御機能が提供されてます。
Google Nest スピーカー、ホームメディア サーバー、IoT(モノのインターネット)デバイスのような接続されたデバイスをホストするホーム ネットワークは、DNS リバインディングと呼ばれる攻撃を受けるおそれがあります。Google Wifi では、この種の攻撃を防止するため、DNS リバインディングに対する保護機能で、公開ドメインにプライベート IP アドレス範囲を使用されないようブロックすることができます。この機能はデフォルトで有効になっています。
試したところ、192.168.10.1 のようなプライベートIPアドレスはブロックされる一方で、127.0.0.1 のようなlocalhostのアドレスはブロックされません。また、当該機能は副作用の可能性もあるため、無効にすることもできます。
リゾルバでのDNSリバインディング対策をどう考えるか
多くのリゾルバがDNSリバインディング対策の機能を提供しますが、デフオルトは無効化されていることから、いずれも「積極的に使っていきましょう」という姿勢ではないように感じます。その典型はbindです。min-cache-ttlの最大値は90秒なので「これで守り切る」という数字ではありません。また、IPアドレスの拒否リストについては以下のような注意書きがマニュアルに付記されています。
The “rebinding” attack must primarily be protected at the application that uses the DNS. For a large site, however, it may be difficult to protect all possible applications at once. This filtering feature is provided only to help such an operational environment; turning it on is generally discouraged unless there is no other choice and the attack is a real threat to applications.
4. BIND 9 Configuration Reference — BIND 9 9.19.0 documentation より引用
リバインディング攻撃は、主にDNSを使用するアプリケーション側で保護されなければなりません。しかし、大規模なサイトでは、可能性のあるすべてのアプリケーションを一度に保護することは困難な場合があります。このフィルタリング機能は、そのような運用環境を支援する目的にのみ提供されています。他に選択肢がなく、攻撃がアプリケーションにとって本当に脅威とならない限り、この機能を有効にすることは一般に推奨されません。(私訳)
この「DNSを使用するアプリケーション側で保護されなければなりません」という指摘に私も同意します。 また、以下の資料にはDNSリゾルバの防御機能の抜け穴が報告されています。例えば、CNAMEとしてlocalhost.を返すようなケースが紹介されています。
このため、DNSリゾルバでの対策はあくまで緩和策としてとらえておくべきだと思います。
結局DNSリバインディング対策はどうすればよいか
ここまで説明したように、DNSリバインディング対策が「出来る」ポイントは以下の4箇所あります。
- 攻撃対象
- ブラウザ
- PROXYサーバー
- リゾルバ
これらの中で、確実な対策がとれるのは攻撃対象のみです。対策自体は難しいものではないので、まずはこれを検討するべきです。 しかし、以下のような状況はありえます。
- 社内ネットワーク内に攻撃対象となり得る機器が多すぎで把握すら難しい
- 対象のサーバーや機器側の仕様上対策が難しい
このため、以下のステップで対策を検討するとよいでしょう。
- ネットワーク内に存在するDNSリバインディング攻撃対象の機器やパソコンの洗い出し
- 対策の要否検討
- 対策方法の検討
- 対策実施
DNSリバインディングはマイナーな攻撃方法であり、他のウイルス感染などの方が簡単に侵入できるため、あまり過敏になることもないとは思いますが、原理的には可能であるので、「重要情報が盗まれる」などの状況は放置しない方がよいでしょう。DNSリバインディングの確実な対策の一つが「認証ちゃんとやる」ですので、結局のところ、
- 社内ネットワークといえども認証とアクセス制御をきちんとやる
この当たり前のことをちゃんとやっていれば大丈夫です。 後は、保険的な対策としてPROXYやリゾルバの対応をどうするかですが、近年はPROXYやリゾルバはセキュリティ製品の一機能である場合も多く、自前でオープンソースソフトウェアを構築するケースは少ないと思います。なので、PROXYやリゾルバでの対策は、積極的に活用するというよりは、「使える場合は使うことも考える」くらいのレベル感ではないでしょうか。
まとめ
DNSリバインディングの対策として使える機能について説明しました。Google社がDNSリバインディング対策に熱心なことが印象的ですが、まずは攻撃可能性の洗い出しと、ローカルネットワークでも認証をおろそかにしないという基本的な対策を推奨いたします。