2010-07-25 2010-07-25
●ツッコミSPAM対策で、ツッコミ抜きのRSSフィードを用意しました
昨日17:47から19:13にかけて、大量のツッコミSPAMが来ています。取り急ぎ、ツッコミの一日あたりの上限数を0にすることにより、対応しましたが、フィードで日記をご覧いただいてる方々には、見苦しいものをお見せして申し訳ございません。
ツッコミ対策は色々工夫してはおりますが、まだ決定的なものはない状態で、時々このような事態となっております。
当面の問題として、RSSフィードがSPAMだらけになることが問題ですので、ツッコミを含まないRSSフィードも生成する設定にいたしました。ツッコミがうざいと思われる方は、恐れ入りますが、以下のURLから再設定いただけるとよろしいかと思います。
http://www.tokumaru.org/d/no_comments.rdf
no_comments.rdfの方は当面試験運用といたします。不具合などありましたら、ツッコミなどでお知らせください。
2010-07-01 2010-07-01
●ぼくがPDOを採用しなかったわけ(Shift_JISによるSQLインジェクション)
PHPのデータベース・アクセス・ライブラリPDOは、DB接続時の文字エンコーディング指定ができないため、文字エンコーディングの選択によっては、プレースホルダを使っていてもSQLインジェクション脆弱性が発生します。
はじめに
本を書いています。SQLインジェクションの節から書き始めて、初稿はレビュアーの方々にお送りしましたところ、さっそく有意義なコメントを多数頂戴しています。ありがとうございます。
その原稿の中で、DBアクセスに使用するPHPのライブラリとしてMDB2を紹介していたところ、PDOを紹介すべきではという意見を複数頂戴しました。そこで、現時点でPDOを採用していない理由を報告したいと思います。これは、「安全なSQLの呼び出し方」でPDOを取り上げていない理由でもあります。
PDOは接続時に文字エンコーディングを指定できない(指定しても無視される)ので、データベースへの接続時の文字エンコーディングはLatin1が暗黙に指定されます。すると、日本語の読み書きで文字化けが生じるため、「SET NAMES SJIS」により、文字エンコーディングを接続後に指定するようなプログラミングがなされているようです(MySQLの場合)。
そのようなプログラム例を示します。このプログラムでは、内部文字エンコーディングとしてShift_JISを利用しているという想定です。PHP5.3.0とMySQL5.1.37の組み合わせで確認しました。
<?php
$dbh = new PDO('mysql:host=localhost;dbname=test;charset=sjis', 'username', 'password');
$dbh->query("SET NAMES sjis");
$sth = $dbh->prepare("select * from test WHERE name=?");
$sth->setFetchMode(PDO::FETCH_NUM);
$name = ...
$sth->execute(array($name));
while ($data = $sth->fetch()) {
var_dump($data);
}
PDO+MySQLの場合、プレースホルダは「動的プレースホルダ」あるいは、俗に「クライアントサイドのプリペアードステートメント」などと呼ばれる実装になっています*1。
このため、$nameとして「ソ' OR 1=1#」という文字列を指定した場合、MySQLに対して以下のようなクエリが送信されます(Wiresharkによるダンプ)。
0x83は「ソ」の先頭バイトです。これをPDOではLatin1(ISO-8859-1)として扱うので、0x83はNBHという制御文字を意味し、1バイト文字になります。PDOは、続く「\'」を「\\\'」とエスケープします。
一方、MySQLサーバー側では、受け取ったSQLをShift_JISと想定しているので「ソ\\'」という並びと解釈します。「\\」は「\」をエスケープしたものなので、後続の「'」は文字列リテラルの終端に使われ、残りの「 OR 1=1#'」はSQL文の一部とみなされます。SQL文が注入できたので、SQLインジェクション脆弱性があるということになります。ここまでの説明を下図にまとめました。
my.cnfの修正でもダメ
「set names」がセキュリティ上問題があることが広く知られるようになったせいか、PDOの文字化けを別の方法で対処するようにすすめているエントリもあります。たとえばこのエントリでは、my.cnf(my.ini)の修正でPDOの文字化けに対応しているのですが、Shift_JISを使う場合はこれもダメです*2。その理由は、「PDOはLatin1として処理したものを、MySQLはShift_JISとして解釈する」状態には変わらないからです。
まとめ
- PDO+MySQLでは、動的プレースホルダ(なんちゃってプリペアード・ステートメント)が使われる
- PDOでは文字エンコーディングが指定できない
- PDO+MySQL+Shift_JISでは、プレースホルダを使ってもSQLインジェクション脆弱性となる
解決策・回避策
PDOの文字化け問題とSQLインジェクション脆弱性問題を解決するにはどうすればよいでしょうか。私が思いつくのは以下の2つの方法です。
- PDOではなくMDB2など文字エンコーディングを正しく扱えるライブラリを使用する(根本的対策)
- アプリケーションからDBまで一貫してUTF-8で処理し、set names utf8により文字化けを回避する(回避策)
2.で具体的な問題が起こるかどうかは分かりませんが、文字エンコーディングを正しく扱えないことで潜在的なリスクがあると考えます。そのため、書いている本の中では1.を採用したという訳です。
しかし、私自身PHPを熟知しているわけではないので、ひょっとすると、PDOの使い方などでもっとよい方法があるかもしれません。識者のご指摘をいただければ幸いです。
追記(2010/07/01 22:20)
id:nihenさんが、PDOに対して文字エンコーディングを設定する方法を調べてくださいました。nihenさん、どうもありがとうございます。以下に引用します。
PDOからよばれるreal_escape_stringで文字コードを考慮させたい場合は
$dbh = new PDO('mysql:host=localhost;dbname=sandbox;charset=cp932', 'sandbox', 'sandbox', array(
PDO::MYSQL_ATTR_READ_DEFAULT_FILE => '/etc/mysql/my.cnf',
PDO::MYSQL_ATTR_READ_DEFAULT_GROUP => 'client',
));
もしくはserver-side-prepareを使う場合(文字コード気にしなくておk)
$dbh = new PDO('mysql:host=localhost;dbname=sandbox;charset=cp932', 'sandbox', 'sandbox', array(
PDO::ATTR_EMULATE_PREPARES => false,
));
ちなむと前者は接続時に使われるオプションなのでnew PDOしたあとにあとから
$dbh->setAttribute
しても、ダメ。後者はおk。
ご覧のように、MySQLサーバーへの接続時に、MySQL APIに渡すパラメータとして、my.cnfのファイル名を指定しています。上記の例ではGROUPを「client」に設定していますので、my.cnfに以下の設定を追加することになります。
[client] default-character-set=sjis
ただし、Windows版のPHPでは、PDO::MYSQL_ATTR_READ_DEFAULT_FILEが使えないようです。このため、サーバー接続時に文字エンコーディングを指定する方法がありません。このため、Windows版のPHPを使う場合は、PDO::ATTR_EMULATE_PREPARESをfalseにする(真のプリペアード・ステートメントを使う)ことで対策することになります。
*1 高木浩光氏のいわれる「なんちゃってプリペアド・ステートメント」
*2 元エントリはUTF-8なので問題はおきませんが、逆に言えば、set names utf8でも問題ないことになります
2010-04-06 2010-04-06
●PROXY(プロキシ)経由でのDNSリバインディングと対策
このエントリでは、PROXY経由でWebアクセスしている場合のDNSリバインディング対策について考察する。
PROXY(プロキシ)経由でWebアクセスしている場合、DNSによる名前解決はブラウザではなくPROXYにより行われる。したがって、 ブラウザ側ではDNS Pinningできないので、PROXY側での対策が重要になる。
広く使われているPROXYサーバーであるsquidの場合、DNS Pinningに相当するパラメータは、negative_dns_ttlであり、デフォルト値は1分間である。したがって、最初のアクセスから1分あけてXMLHttpRequestすれば、DNSリバインディングが成立する。これは実験で確認した。Internet ExplorerやOperaは、他のブラウザと比べてDNS Pinningがしっかりされている印象があるが、PROXY経由の場合は、これらブラウザでも比較的簡単にDNSリバインディングが成立するので注意が必要だ。
先のエントリで説明したように、JavaアプレットによるDNSリバインディングでも、URLクラスを使いかつPROXY経由でのアクセスの場合は、やはりPROXYでのDNS Pinningが問題となる。PROXY経由でのアクセスでは、Javaアプレットの場合でも、JavaScriptのXMLHttpRequestの場合同様、1分以上間隔をあけてやればDNS Rebindingが成立する。ただし、攻撃に使用できるプロトコルはHTTPのみであるので、JavaアプレットとJavaScriptでは、リスクは変わらない。
negative_dns_ttlの値を大きくすれば、squidがDNSキャッシュする最低時間を延ばすことが可能だが、二つの点で問題がある。
第一の問題は、negative_dns_ttlを大きくすることの副作用だ。negative_dns_ttlは元々、DNSアクセスに失敗した場合のキャッシュ保持時間を意味するからだ。以下に、negative_dns_ttlのリファレンスを引用する。
Time-to-Live (TTL) for negative caching of failed DNS lookups. This also sets the lower cache limit on positive lookups. Minimum value is 1 second, and it is not recommendable to go much below 10 seconds.
[squid : negative_dns_ttl configuration directiveより引用]
このため、一例として、negative_dns_ttlを6時間に設定した状態でDNS参照に失敗した場合(参照先DNSサーバーが一時的に停止した場合など)も6時間はDNSを再アクセスしない。すなわち、DNSのエラーが発生した場合は、negative_dns_ttlで設定した時間内は、当該サイトにアクセスできなくなるのだ。このため、negative_dns_ttlの値をあまり大きくすると、サイト閲覧に支障がでる可能性がある。
第二の問題は、negative_dns_ttlを長くしてもDNSリバインディングの根本対策にはならないことだ。 その背景には、PROXYサーバーをマルチユーザで使う場合が多いことや、タブブラウザの普及などがある。 やはりnegative_dns_ttlを6時間に設定した場合で説明しよう。AさんとBさんが同じPROXYサーバーを共有しているとする。Aさんが10:00にワナサイトにアクセスした場合、PROXYは16:00までワナサイトのIPアドレスをキャッシュする。したがって、Bさんが15:59にワナサイトを閲覧すると、そこから一分間でDNSキャッシュが無効になり、Bさんのブラウザ上動作するXMLHttpRequestがプライベートアドレスにアクセスすることを許してしまうかもしれない。 また、シングルユーザで考えても、タブブラウザのユーザはタブを開きっぱなしにする傾向があるので、6時間後の攻撃が成立する可能性はある。
アクセス制御による対策
結局negative_dns_ttlによる対策はうまくいかないので、別の方法を考える必要がある。有効な対策は、proxyのアクセス制御(ACL)により、squidにプライベートアドレスを中継させないことだ。以下に、192.168.0.0/16に対してアクセスを禁止する場合のsquid.confの設定例を示す。
acl localnet dst 192.168.0.0/16 http_access deny localnet
この場合、プライベートアドレスに対してはPROXY経由でアクセスできなくなるので、ブラウザのPROXY設定の例外設定をお忘れなく。下図に例外設定の例を示す。

このACLによる対策の効果であるが、JavaScriptとJavaによるアクセスに関しては完全に対策できると考える。当初Javaアプレットが対策から漏れることを心配していたが、先のエントリで検証したように問題ないことがわかった。Flashについては未検証なので今後検証したい…ところだが、私はFlashのコンテンツを書いたことがない。どなたか教えてください。
まとめ
PROXY経由でWebアクセスしている場合のDNSリバインディング対策について検討し、PROXYのACLによりプライベートアドレスを中継禁止することで対策可能であることを示した。DNSリバインディング対策のまとめについては別稿にて報告する。
2010-04-05 2010-04-05
●JavaアプレットのDNSリバインディングはJRE側で対策済みだった
Black Hat Japan 2007における金床氏のプレゼンテーションや金床本で、Javaアプレットに対するDNSリバインディングの手法が説明されている。ここしばらく、その追試を行っていた。結論としては、金床氏の方法にはJRE側で対策がとられていたので報告する。
金床氏が報告しているDNSリバインディングの手法とは以下のようなものだ(金床本に説明されている方法)。
- 被害者のユーザにワナのページをPROXY経由で閲覧させる
- ワナのドメイン(FQDN)に対するIPアドレスを変更する
- ワナページ上で数秒〜数十秒のちにJavaScriptによりJavaアプレット用のHTMLをレンダリングする
- JREがアプレットをPROXY経由で*1ダウンロードする
- アプレットのダウンロード時点で既にDNSレコードは書き換わっているが、PROXYがDNSキャッシュしているため、アプレットは正常にダウンロードされる
- アプレットからワナサイトのFQDNの任意のポートに対してSocket通信する。
- 既にIPアドレスは書き換わっているので、プライベートアドレスに対して任意のSocket通信ができることになる。
このように、攻撃が成立すればとても凶悪な攻撃となるのだが、最近のJREでは以下の方法で対策されているようだ。
- アプレットがPROXY経由でダウンロードされた場合、アプレットからのSocket通信は全てクロスドメイン通信と扱われる
このような挙動に変わったバージョンを調べたところ、Java SE 6 update 3以降であることが分かった*2。すなわち、Javaアプレットの金床方式によるDNSリバインディングが成立するバージョンは以下の通りである。
- J2SE1.4.xの全てのアップデート
- J2SE5.0の全てのアップデート
- Java SE 6のUpdate2以前
そして、Java SE6 Update3以降で、クロスドメイン通信になった場合の挙動は、JREのバージョンにより異なる。Java SE 6 update 10より前のバージョン(Update3〜7)だと、Socketでダイレクトにアクセスしようとするとエラーになる。一方、Java SE 6 update 10以降のバージョンでは、アクセス先のcrossdomain.xmlを参照して、その内容に従う。このあたりの挙動は、「次世代 Java(TM) Plug-In テクノロジのリリースノート (JDK 6u10)」を参照されたい。
一方、アプレットが直に(PROXYを経由せずに)ダウンロードされた場合は、アプレットの置かれたFQDNに対してSocketによる任意ポートへの通信が可能だ。この場合は、DNSリバインディングが懸念されるところであるが、Javaの強固なDNS Pinningにより、アプレットダウンロード時のIPアドレスが永続的に保持されるので、DNSリバインディング攻撃はできない。
したがって、金床氏のプレゼンテーション資料には、Java版DNSリバインディング対策が色々書かれているが、その後Java側で対策がとられたことを考慮すると、ユーザが取るべき最善の対策は以下だろう*3。
- JREを最新のバージョンにアップデートする
J2SE5.0以前のバージョンは既にEOLになっているし、DNSリバインディング対策もとられていないことから、使うべきではない。
また、Socketクラスではなく、URLクラス等HTTP系のクラスを使う場合は、PROXY経由の有無にかかわらず元FQDNとの通信は可能である。URLクラスを使う場合は、アプレットダウンロード時の設定に従い、PROXY経由となるか否かが決まる。URLクラスで、PROXY経由しない場合は、JavaのDNS Pinningが有効となり、DNSリバインディングはできない。一方、PROXY経由の場合は、PROXY側のDNS Pinningが問題となる。PROXYによるDNSリバインディング対策については稿を改めて説明する。
*1 Javaの設定にもよるがデフォルトはブラウザの設定をそのまま利用するので、PROXY経由となる
*2 Java SE 6 update 3がリリースされたのが2007年10月3日、金床氏のBHJでのプレゼンテーションが2007年10月25日だったので、BHJのプレゼンテーション時点でJRE側の対策がなされていたことになる。ただし、update 3のリリースノートを読んでも、このような対策がなされていることは読み取れなかった。おそらく金床氏もupdate 3で対策されたとは明確には分からなかったのだろう。金床氏のJavaアプレットによるDNSリバインディングのデモページには「Does not work on JRE1.6.0_03 or later」と明記されている。
*3 今時Javaアプレットなど使わないというのであれば、あわせてJavaアプレット(プラグイン)を無効にするというのもあり。その場合もJREのバージョンは最新にすべきだ。
2010-03-29 2010-03-29
●DNSリバインディングによる無線LANパスフレーズの読み出しに成功
DNSリバインディング攻撃により、無線LANアクセスポイント(AP)に設定したWPAのパスフレーズ(PSK)を読み出す実験に成功したので報告する。
先のエントリではDNSリバインディング攻撃によるルータ設定変更の実験を報告したが、エントリでも触れたように設定変更についてはCSRF脆弱性を悪用することによっても可能だ*1。両手法の違いとして、CSRFは攻撃先の情報を読み出すことはできないのに対して、DNSリバインディングでは読み出しも可能だ。DNSリバインディングによるセッションCookieの読み出し例については、「ケータイtwitter(twtr.jp)においてDNS Rebinding攻撃に対する脆弱性を発見・通報し、即座に修正された」でも言及した。
無線LANアクセスポイントに対するDNSリバインディングのリスクを考える上で、もちろん設定変更もリスクではあるが、設定変更された場合、元々の所有者が無線LANにアクセスできなくなるなどの副作用が想定される。一方、パスフレーズの読み出しが行えれば、被害者のユーザに気づかれないままに攻撃される可能性が高く、それだけリスクも大きくなる。
また、Wi-Fiの普及に伴い、ゲーム機や携帯電話などでも広く無線LANが利用できるようになったことを受けて、無線LAN機器メーカー各社は無線LANの簡易設定方式を提唱・実現している。これはよいことだが、一方で、誰も無線LANの設定画面を見たこともなく、APの管理用パスワード(WEPやWPAのパスフレーズではない)がデフォルトのままになっているケースが大半であると予想される。これは危険な状態ではないか。
AOSSによる簡単設定
筆者が実験用に所有しているAPは、株式会社バッファローのWLA2-G54Cである。auの携帯電話biblioのWi-Fi機能を評価するために購入したものだ。バッファロー製APの特徴としてAOSSという簡単設定機能により、ボタン一つで無線LANの設定が可能である。その一端を以下に示そう。
APの設定でまず行うことは、APのIPアドレスの設定だ。ブラウザでもできなくはないが、APのユーティリティを使用すれば簡単だ。

上図でパスワードを入力する欄があるが、これは現在の管理パスワードを入力する欄でデフォルトは空である。ここで管理パスワードを設定できるわけではない。その後、バッファローの接続ユーティリティからAOSSによるプロファイルの追加を指定すると以下のような画面となる。

この状態でAPのAOSSボタンを長押しすると、APとクライアントのネゴシエーションが始まり、SSIDや暗号化の方法、パスフレーズなどが自動的に設定される。

安全な設定が簡単に行えるのだが、この手順に従うと、APの管理画面を一度も起動することなく、またAPの管理パスワードを設定する機会もないのだ。AOSSはPC以外に、ゲーム機(ニンテンドーDSなど)や携帯電話でも利用できるもので、おそらくバッファロー製無線LAN製品のユーザの大半が、管理パスワードを設定しないままに使用していると予想される。
APの管理画面からパスフレーズが見える
この状態で、APの管理画面をブラウザから表示させる。無線LANのメニューを表示すると以下のような画面だ。

これを見ていやな予感のしたあなたの勘は正しい。HTMLソースを表示させると以下のような部分がある(一部マスク表示にした)。
<input name="wl_wpa_psk" type="password" maxlength="64" size="56" value="7c188cXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX67">
すなわち、管理画面が起動できれば、パスフレーズを見ることができるのだ。実は、これはこれで便利な面もあって、AOSSで設定したパスフレーズを確認することにより、AOSS対応でないクライアントを手動設定できるのだが、そういう利用方法を意図したものとも思えない。ともかく、ここまで紹介した内容から以下のことが分かる。
- AOSSによる設定では管理画面を見る必要がない
- 管理画面のデフォルトパスワードは空である
- 管理画面にログインすると無線LAN暗号化のパスフレーズが参照できる
これらの事実から、バッファロー製AP WLA2-G54Cは、DNSリバインディング攻撃により無線LAN暗号化パスフレーズが読み出せると判断し、実験してみることにした。BCNの調査によると、バッファロー社は無線LAN分野の2009年のシェアが53.0%ということで、影響も大きいと思った。
侵入実験の実際
以下に、侵入実験に用いたスクリプトの一部を示す。ドメインは仮のものに置き換えた
// リクエスタのセット
var requester = new XMLHttpRequest();
requester.open('GET', 'http://root:@wlan.example.com/advance/ad-lan-wireless_sec_g.htm');
...
// パスフレーズの切り出し
var response = requester.responseText;
var regexp = /wl_wpa_psk.*value="([^"]+)"/m;
if (response.search(regexp) != -1) {
pass = RegExp.$1;
}
このスクリプトはインターネット上のワナサイトに置かれる。このコンテンツが読み出された直後に上記FQDNに対するIPアドレスをAPのプライベートアドレスに書き換える。コンテンツ読み出しから4分後に上記リクエストが送出されるようにスクリプトを作成した。実行例を以下に示す。

図示したように、パスフレーズが読み出されていることが分かる。この後、ワナサイトにこの値をPOSTして、攻撃は完成である。
対策
バッファロー製AP WLA2-G54Cをマニュアルの指示通りに設定した状態において、DNSリバインディングにより暗号化パスフレーズ(PSK)を読み出すことに成功した。ユーザ側の対策としては、以下に尽きるだろう。
管理パスワードを適切に設定する
しかし、コンシューマ向け製品ということを考慮すると、現在の製品仕様のままでは全てのユーザに管理パスワードを徹底させることも難しいだろう。したがって、製品提供者側の対策が今後は重要となる。以下のような対策案が考えられる。
- APの管理パスワードの初期値を機器毎に違うものにする(根本的対策)
- 製品設定の流れの中で管理パスワードの変更を強制する(根本的対策)
- 管理画面URLのホスト部は数値IPアドレスのみを許容する(保険的対策)
まとめ
バッファロー製AP WLA2-G54Cを対象として、DNSリバインディング攻撃により暗号化パスフレーズ(PSK)を外部から読み出せることを確認した。他社製品の場合は、パスフレーズが読み出せるかどうかは不明であるが、マニュアルを読む限りは管理パスワードの初期値が空で、変更も要求されないものが多いよう*2なので、仮に暗号化パスフレーズが読み出せないとしても、設定変更は可能である可能性が高い。この場合、APの設定を「暗号化なし」に設定変更することにより、内部ネットワークへの侵入や盗聴などが行える。
無線LANの簡単設定機能は、AOSS以外にも「らくらく無線スタート」やWPSなどがある。これらは、安全な無線LAN設定を簡略化するという意味で社会的な意義のあるものだと思うが、基本的な想定として、「管理画面にはインターネットからアクセスできないので管理パスワードは設定しなくても大丈夫」と考えられているように見受ける。しかし、DNSリバインディングやクロスサイト・リクエスト・フォージェリ(CSRF)などの受動的攻撃により、インターネット越しに管理画面にアクセスすることは可能だ。ルータにCSRF脆弱性が多数指摘されてきた歴史は、「site:jvn.jp クロスサイト・リクエスト・フォージェリ ルータ」でGoogle検索してみると直ぐ分かる。DNSリバインディングについては、パスワードの設定さえすれば対策できるため、製品の脆弱性としては指摘しにくいが、今後十分発生し得る脅威として、メーカー側には対策を要望する。
2010-03-25 2010-03-25
●DNSリバインディングによるルータへの侵入実験
このエントリでは、DNSリバインディング攻撃によりブロードバンドルーターの設定を外部からの侵入を許すように変更してみたので報告する。
DNSリバインディングに対する関心が高まってきている。本年1月12日には、読売新聞夕刊一面トップで、iモードブラウザ2.0のDNSリバインディングによる不正アクセスが報じられた。3月17日のcomputerworld.jpでは、『2010年に最も警戒すべきセキュリティ脅威は「DNSリバインディング」』という翻訳記事が紹介された。実は「最も警戒すべきセキュリティ脅威」というタイトルは誤報だったわけだが*1、DNSリバインディングというマニアックな攻撃手法が注目を浴びるきっかけにはなったと思う。
そこで、専門家の方にはいまさらかもしれないが、DNSリバインディング攻撃により、自宅のブロードバンドルーターの設定を変更してみた。思いの外簡単にできたが、対策も容易であるので、その両方を説明しよう。
侵入実験の前提
まず一般的なDNSリバインディングの概要説明は、「DNS Rebinding」を参照頂きたい。この説明では、イントラネット内のサーバーへの侵入の例を挙げているが、今回の実験では、ブロードバンドルータへの侵入となる。実験に用いた自宅のルータは、プライベートアドレス(内部ネットワーク)からは設定が変更できるが、インターネット側からは設定できないという仕様となっている。そのため、DNSリバインディング攻撃が有効だ。この種のルータに対する攻撃手法としてはクロスサイト・リクエスト・フォージェリ(CSRF)という選択肢も考えられるが、このエントリでは言及しない。
実験では、まず、ルータの設定変更に必要なHTTPリクエストを調査した。その結果、POSTリクエストを3回送信すれば、外部からの侵入を許すように変更できることがわかった。
次に、インターネット上にワナサイトを用意して、ワナサイトをリクエストした後にDNSの内容を書き換え、一定時間置いた後に上記3つのPOSTリクエストを送出するようにした。実験に用いたブラウザはFirefox 3.6.2であり、最初のリクエストから4分間の間隔をとれば、DNSリバインディングが成立することがわかった*2。このため、3回のリクエストはそれぞれ、4分後、4分10秒後、4分20後に送出されるように、JavaScriptによるスクリプトを作成した。
ここで問題になるのが、ルータの認証をどうやってすり抜けるかだ。他の攻撃手法、たとえばCSRFの場合は、ユーザが認証している状態を悪用するわけだが、DNSリバインディングではこの方法は使えない。なぜなら、DNSリバインディングによるリクエストのHost:フィールドはワナサイトのドメインになっているからだ。このため、実験では、攻撃者がルータのパスワードを推測できるという想定にした。以下の説明では、ユーザIDとパスワードがともに「admin」になっていると想定する。これはそれほど突飛な想定ではない。この種のルータの初期設定はパスワードなしか、固定の初期パスワードになっていることが多いからだ。
侵入実験の実際
下図に侵入前のルータの設定を示す。赤い四角で囲った部分に、新たな設定を追加する。

次に、以下は侵入実験に用いたスクリプトのごく一部だ。このルータはBASIC認証が使われているので、以下のようにURLの中にBASIC認証のIDとパスワードを記述した。スクリプトキディが真似するとまずいので、スクリプトの全容は非公開とする。また、router.example.comはワナサイトのドメインだが、仮にものに置き換えた。
var requester = new XMLHttpRequest();
requester.open('POST', 'http://admin:admin@router.example.com/entry_ipnat_main_bottom.html');
このスクリプトを含むワナが閲覧されたら直ぐに、router.example.comのIPアドレスを192.168.0.1(ルータのアドレス)に書き換える。その後3回のリクエストが正常にルータに送信されると、設定を反映するためにルータがリセットされた。攻撃成功である。攻撃成功後のルータの設定を下図に示す。

ご覧のように、192.168.0.10の端末に対して、インターネットからTCP3389ポートを受け付けるようになった。3389はWindowsのリモートデスクトップが使用するポートなので、この設定変更により外部からの侵入が可能になった。
対策
ルータやファイアウォールの設定変更は、DNSリバインディングのデモとしては定番と言えるものだが、実際の結果を見るとギョッとした人も多いのではないか。しかし、幸いなことに、対策は容易である。前述のように、DNSリバインディング攻撃は、ユーザの認証状態を引き継ぐことができないので、認証がかかっているサイトの場合、パスワードが分からないと侵入できない。従って、良質のパスワードを設定するだけで、侵入を食い止めることができる。
しかし、問題は、これだけブロードバンドのインターネットが普及した状況下で、ブロードバンドルータの多くがデフォルトパスワードのまま(パスワードなしも含めて)の設定と予想されることだ。従って、DNSリバインディングによる攻撃はいつ起こっても不思議ではないし、既に起こっているかもしれない。
従来、ブロードバンドルータのパスワード初期設定がいい加減だった理由は、「外部からは設定変更できないから安全だ」という意識が働いていたからだと推測する。しかし、ルータに対する外部からの侵入手法は、DNSリバインディングの他、ルータのCSRF脆弱性を悪用するなど複数存在する。一方で、ブラウザ側でDNSリバインディングを完璧に対策するのは困難だ。その理由は、IPアドレスの変更は、DNSの正式な仕様として認められた挙動であるからだ。このため、ルータやファイアウォールの初期パスワードやパスワードの管理について、さらなる工夫が求められる。
さらに網羅的なDNSリバインディング対策については、稿を改めて説明する。
Before...
★ bero [> PDO+MySQLの場合、プレースホルダは「動的プレースホルダ」あるいは、俗に「クライアントサイドのプリペアード..]
★ bero [上記mysql->libmysql ドキュメントにも書いてた http://php.net/manual/ja/r..]
★ carrot [PHP5.2.1から、PDO_MYSQLは、デフォルトではクライアントサイドのプリペアードステートメントになったよう..]