PHPカンファレンス2011にて講演します

PHPカンファレンス2011講演公募枠に応募して採択いただきましたので、講演することになりました。9月10日(土)、場所は蒲田の大田区産業プラザ PiOです。詳細は、講演プログラムをご覧ください。
タイトルは、「徳丸本に学ぶ 安全なPHPアプリ開発の鉄則2011」ということで、拙著「体系的に学ぶ 安全なWebアプリケーションの作り方」の中から、安全なPHPアプリケーション開発のための「10の鉄則」という形で提示いたします。以下は応募の際に提出したアジェンダです。

PHP本体の脆弱性はかなり少なくなってきましたが、PHPを使ってアプリケーションを開発する際には、陥りやすい落とし穴がまだまだ多いのが現状です。本発表では、拙著「体系的に学ぶ 安全なWebアプリケーションの作り方」を題材として、安全なPHPアプリケーション開発のための最新情報を「10の鉄則」という形で紹介します。

去年の文字コードセキュリティネタはとんがったテーマでしたが、今回はPHPアプリケーションのセキュリティ全般のお話について、最新動向を交えながら説明致します。
10の鉄則がなにかは当日のお楽しみですが、以下のテーマは交えようと思っています。

  • 入力値検証とセキュリティの微妙な関係
  • 安全なPDOの使い方(徳丸本からのアップデート)
  • パスワードの保存方法についてのベストプラクティス

40分の時間枠で盛りだくさんな話題となりそうです。当日は駆け足の説明になりそうですので、あらかじめ徳丸本で予習をしていただくと理解しやすくなるでしょう(宣伝)。
カンファレンス申し込みはこちらからどうぞ。参加は無料、懇親会のみ有料となります。

[PR]

phpMyAdminにおける任意スクリプト実行可能な脆弱性の検証

 phpMyAdmin(3.3.10.2未満、3.4.3.1未満)には、リモートから任意のスクリプトが実行可能な脆弱性があります。このエントリでは、この脆弱性が発生するメカニズムと対策について報告します。

概要

 phpMyAdminには下記の2種類の脆弱性の組み合わせにより、リモートから任意のスクリプトを実行させられる脆弱性があります。

 該当するバージョンは以下の通りです。

 影響を受ける条件は、上記バージョンのphpMyAdminを使用していることに加えて、以下をすべて満たす場合です。

影響

 外部から、PHPの権限で任意のスクリプトを実行できます。検証イメージについては、NTTデータ先端技術株式会社から公表されている検証レポートを参照下さい。
 公開されているPoC(概念実証コード)は、そのままの形で悪用でき、脆弱の有無確認から任意スクリプト実行の裏口作成までを行うもので、影響が大きいと考えられます。上記に該当するサイトは、後述する対策を直ちに実施して下さい。

CVE-2011-2505の概要

 脆弱性の詳細は、このエントリに説明されていますが、以下にもう少し掘り下げて説明します。
 CVE-2011-2505は、任意のセッション変数を外部から任意の値に変更できるというものです。この説明を見て、「register_globalsでも有効になっていたのか?」と疑問に思った方は惜しいところまでいっていますが、register_globalsが原因ではありません。
 該当のソース(libraries/auth/swekey/swekey.auth.lib.php 266行目以降)は以下の通りです。

if (strstr($_SERVER['QUERY_STRING'],'session_to_unset') != false)
{
    parse_str($_SERVER['QUERY_STRING']);
    session_write_close();
    session_id($session_to_unset);
    session_start();
    $_SESSION = array();
    session_write_close();
    session_destroy();
    exit;
}

 ここで、parse_strという関数が使われています。この関数は、クエリストリング形式の文字列を入力として、それを解釈し、PHPの変数として代入するものです(省略可能な第2引数を指定した場合は、そこに配列として値が返ります)。具体例で説明すると、入力が'a=xyz&b[x]=p23&_SESSION[user]=yamada'の場合、以下の結果となります。

<?php
  session_start();
  parse_str('a=xyz&b[x]=p23&_SESSION[user]=yamada');
  var_dump($a);
  var_dump($b);
  var_dump($_SESSION);

【実行結果】
string(3) "xyz"
array(1) {
  ["x"]=>
  string(3) "p23"
}
array(1) {
  ["user"]=>
  string(6) "yamada"
}

 先に引用した箇所には、parse_str($_SERVER['QUERY_STRING']); という行があるので、URL上のクエリ文字列により、任意の変数を設定できることになります。このスクリプトの最後はexit;でアプリケーションを終了させるので、通常の変数の設定には攻撃としての意味はありませんが、セッション変数が変更できることが問題です。セッション変数は、後続のページに影響を与えるからです。
 これが、CVE-2011-2505の「変数汚染」脆弱性の原因です。
 なお、$_SESSION = array(); という行があるので、セッション変数をクリアしているように見え、セッション変数の汚染はできないように思えますが、実際には汚染が可能です。その理由は、以下の通りです。

  • session_write_close(); の呼び出しにより、セッションの内容を書き出している
  • その後、session_id($session_to_unset); によりセッションIDを変更しているので、セッションの内容は上書きされない
  • 攻撃者は元のセッションIDに戻すことにより、汚染した方のセッションを悪用できる

 ここで、$session_to_unset という変数はセットされていないように見えますが、クエリ文字列session_to_unsetをセットすれば、parse_str関数によりこの変数がセットされるという流れです。分かりにくいプログラムですね。
 ここで、この脆弱性を修正したバージョン(3.4.3.1)の該当箇所を引用します。

if (!empty($_GET['session_to_unset']))
{
	session_write_close();
	session_id($_GET['session_to_unset']);
	session_start();
// 以下同じ

 これなら分かりやすいですね。そして修正前のスクリプトでは、parse_str関数の呼び出しが、変数session_to_unsetの設定だけに使われていたことが分かります……。なんと言いますか、鶏を割くにいずくんぞ牛刀を用いん、の類で、単にクエリストリングsession_to_unsetを使いたいだけなにのにparse_str関数を呼び出して、プログラムは読みにくくなるし、脆弱性は混入するし、でいいことはまったくありません。最初からこう書けばいいのに、そうしなかった理由は、昔からのregister_globals前提のスクリプトを、register_globalsを設定しなくても動くように変更したためでしょうか(憶測)。
 これで、CVE-2011-2505の説明は終わりです。

CVE-2011-2506の概要

 続いて、CVE-2011-2506の説明です。こちらは、ConfigGenerator.class.phpというファイルの中でセッション変数の内容をPHPのコメントに書き出している箇所があり、そこに脆弱性があります。該当箇所のソース(setup/lib/ConfigGenerator.class.php 38行目)を引用します。

    // servers
    if ($cf->getServerCount() > 0) {
        $ret .= "/* Servers configuration */$crlf\$i = 0;" . $crlf . $crlf;
        foreach ($c['Servers'] as $id => $server) {
            $ret .= '/* Server: ' . strtr($cf->getServerName($id), '*/', '-') . " [$id] */" . $crlf

 引用の最下行で、「/* Server:」で始まるコメントを書き出しています。生成されるコメントの例を以下に示します。

config/config.inc.php

/* Server: the test server [1] */

 $cf->getServerName($id)が、「the test server」、$idが「1」です。
 ここで、前者の方で、strtr関数で「*」を「-」に変換しているのは、スクリプトインジェクション攻撃対策のサニタイジングです。ここに以下の文字列を挿入した場合、

*/ system('echo /etc/passwd'); /*

 サニタイズしない場合、生成されるコメントは以下となります。コメントが閉じられ、system関数によりOSコマンドが実行されます。

/* Server: */ system('echo /etc/passwd'); /* [1] */

 一方、サニタイズした場合は、コメントは以下となり、スクリプトの注入はできません。

/* Server: -/ system('echo /etc/passwd'); /* [1] */

 なお、該当箇所の開発者はstrtr関数の仕様を勘違いしているようです。strtr関数は文字単位の置換であり、第2引数に指定された文字を第3引数に指定された文字に変換するものですが、文字列中の位置に意味があり、第2引数のn番目に指定された文字を第3引数のn番目の文字に変換します。先の例では、第2引数が「*/」、第3引数が「-」ですが、第2引数の方が長い文字列の場合はそれは無視されます*1。すなわち、「*」を「-」に変換するという意味になります。開発者の意図が、仮に「*」と「/」の両方を「-」に変換することである場合は、第3引数は、「--」でなければなりません。このあたり、プログラムがちょっとずつ甘いなぁと思いますが、幸いこれは脆弱性ではありません。
 問題は、$idをサニタイジングしていないことにあります。このため、セッション変数の汚染により$idの方にスクリプトインジェクション攻撃を仕掛けることができるというのが、CVE-2011-2506の脆弱性です。
 ここで、この脆弱性を修正したバージョン(3.4.3.1)の該当箇所を引用します。該当する最後の行だけです。

            $ret .= '/* Server: ' . strtr($cf->getServerName($id) . " [$id] ", '*/', '-') . "*/" . $crlf

 ご覧のように、「$cf->getServerName($id) . " [$id] "」をまとめてサニタイズするようにしています。これで問題はないと思いますが、strtr関数の使い方は相変わらず間違っています。
 CVE-2011-2506の説明は以上です。これまで説明したように、CVE-2011-2505脆弱性でセッション変数を汚染して、CVE-2011-2506脆弱性を悪用してPHPスクリプトを書き出すというのが攻撃の流れになります。
 ここで、読者の参考のため、PoCの攻撃手順のHTTPリクエストを以下に示します。

GET /phpmyadmin/setup/index.php                                  セッションID、トークンの取得など
GET /phpmyadmin/?_SESSION[ConfigFile][Servers][*/攻撃スクリプト  セッション変数汚染
POST /phpmyadmin/setup/config.php                                攻撃コードの埋め込み
GET /phpmyadmin/config/config.inc.php?eval=攻撃コード            攻撃コードの実行

対策

 上に示したように、スクリプト注入の攻撃が成立するためには、攻撃者がphpMyAdminのsetupディレクトリとconfigディレクトリの配下のスクリプトにアクセスする必要があります。しかし、これらのディレクトリは、phpMyAdminの設定用のスクリプトがおかれている訳ですから、外部から認証なしにアクセスできること自体が、そもそも脆弱であると言えます*2このことから、phpMyAdminバージョンに関わらず、以下を強く推奨します。

  • phpMyAdminのインストールされたディレクトリには外部からアクセスできないよう適切なアクセス制限を設ける

 具体的には以下が考えられます。

 上記のHTTP認証を書ける場合の注意を補足します。phpMyAdmin自体にもHTTP認証の機能がありますが、それは使用せずに、Apache等の設定でphpMyAdminディレクトリ以下全体にHTTP認証をかけるようにします。その理由は、phpMyAdminのHTTP認証はあくまでアプリケーションの認証であり、setupスクリプトやconfigディレクトリに関してはアクセス制限は掛からないからです。このため、Apache等でディレクトリ単位のアクセス制御を行い、phpMyAdmincookie認証を設定するのがよいと思います。
 また、configディレクトリは、普段は必要のないものなので、設定が終わったら削除するべきです。これだけでも、このエントリで紹介したスクリプトインジェクションは防げます。

 このような対策の一方で、phpMyAdminを最新版にバージョンアップして下さい。

まとめ

 phpMyAdminスクリプト実行の脆弱性(CVE-2011-2505、CVE-2011-2506)について報告しました。
 私がこの脆弱性に関心をもったきっかけは、スクリプトが実行される脆弱性とはどのようなものなのだろうという興味からでした。もちろん、ファイルインクルードとか、evalインジェクション、あるいはアップローダ脆弱性など、任意スクリプトが実行できる脆弱性はいくつかあるわけですが、phpMyAdmin脆弱性はどれでもないように思えたからです。
 調べた結果はご覧の通りですが、私は、設定ファイルをPHPスクリプトとして生成するというphpMyAdminの設計がそもそも危なっかしいと思います。設定ファイルはテキスト形式にしておけば、スクリプトインジェクションの可能性もありません。おそらく、導入を簡単にするための工夫なのだと思いますが、よくない設計だと思います。
 また、parse_strの使用も問題です。第2引数をとらないparse_strは、常に変数汚染の危険性が伴いますが、phpMyAdmin3.4.3.1のソースを調べたところ、parse_strを使っている箇所がまだ1つ残っていました(libraries/auth/swekey/swekey.auth.lib.php 146行目)。脆弱性となるかどうかは調べていませんが、潜在的に危険であると言えます。

 しかし、私が一番問題だと思うのは、phpMyAdminを危険な状態で設置しているサイトが少なからずあることだと思います。phpMyAdmin自体に認証機能があるので油断している利用者がいるのかもしれませんが、それは危険です。phpMyAdminはインターネットに公開して使用されることを想定した設計になっていないように思います。phpMyAdminはインターネット越しに使わないのが一番よいと思いますが、レンタルサーバーなどでやむを得ずインターネット越しにphpMyAdminを利用する場合は、IPアドレス制限やHTTP認証などを併用するべきです。
 加えて、configディレクトリは設定終了後削除しておくべきです。configディレクトリが残っている状態でphpMyAdminを起動すると、以下のようなエラーメッセージが表示されます。

 このような警告メッセージを放置することは非常に危険だということです*3

 まとめると、phpMyAdminスクリプトインジェクション攻撃を受けるのは、以下の三重苦が揃うことが原因だと考えます。

[PR]

*1:PHPオンラインマニュアルによると、「from と to の長さが異なる場合、長い方の余分な文字は無視されます。」とあります

*2:これら設定用のスクリプト自体には認証機能はありません。

*3:phpMyAdminのどのバージョンからこのメッセージが表示されるかは調べていません

EZ番号に関する注意書きが変更された

 今日、KDDIのEZfactoryのEZ番号に関する説明が変更されていました。

2011年6月9日の内容(URLは魚拓)赤字はママ

HTTP Requestヘッダ情報では、この他にHTTP_X_UP_SUBNOフィールドにてEZ番号が確認できます。
EZ番号は、ユーザの端末操作によって、送出しない設定にすることが可能です。ユーザが「送出しない」設定にした場合、本フィールドは送出されません。

※EZ番号に関する注意
EZ番号はスマートフォン等では詐称可能なため、ユーザー認証に用いる場合には、EZ番号と併せてEZサーバのIPアドレス等、複数手段で確認してください。

http://megalodon.jp/2011-0609-1231-51/www.au.kddi.com/ezfactory/tec/spec/4_4.html


本日2011年7月15日の内容。赤字指定は筆者。

EZ番号とは、EZweb契約ごとにユニークに付与されるIDです。例えば、ユーザーの判別などに利用することができます。

IPアドレス/ユーザーエージェントでのフィルタ、ID/パスワード認証を併用するなど、サイト内容に応じた適切な方法でご利用ください。

EZ番号の情報は、「HTTP Requestヘッダ」の「HTTP_X_UP_SUBNO」フィールドに格納され、Webサーバに送信されます。
EZ番号はユーザーの端末操作によって、送出しない設定にすることが可能です。ユーザーが送出しない設定にした場合、本フィールドは送出されません。

※2011年秋冬モデル以降の一部機種ではPCSVとEZブラウザのIPアドレス帯域は統合されますが、例えば、ユーザーエージェントを組み合わせることでブラウザを判別することができます。

http://www.au.kddi.com/ezfactory/tec/spec/4_4.html


6月15日の日記では以下のように書きました。

これはダメでしょう。EZ番号をユーザ認証に用いる状況をキャリアとして公式に認めている一方で、安全な確認方法は具体的に示されていないからです。

http://d.hatena.ne.jp/ockeghem/20110615/p1

 本日改訂された内容は、「EZ番号をユーザ認証に用いる状況をキャリアとして公式に認め」ないように、婉曲な表現になりましたね。
 au/KDDIは、EZ番号による認証を保証していない(かつては保証しているように受け取れるような表現だったが、否定された)と見て良いでしょう。EZ番号による「かんたんログイン」は推奨しませんが、仮に実装した場合は、事故があってもサイト運営者の責任ということになります。

[PR]

勝手に補足:レガシーPHPのセキュリティ対策,大丈夫ですか?未修正の脆弱性(2)

 第4回 未修正の脆弱性(2):レガシーPHPのセキュリティ対策,大丈夫ですか?|gihyo.jp … 技術評論社という記事を読みました。とても重要なことが記載されている一方で、記述の間違い及び記述もれがあるため、読者の便宜のため勝手に補足したいと思います。具体的に、PHP4系の最終版PHP4.4.9にてパッチの提供されない4つのぜい弱性についての話題です。一部のぜい弱性はPHP5.2系でもパッチが出る見込みがないものです。

CGIDoS

 元記事ではCVE-2009-3660として紹介されていますが、CVEおよびNVDのサイトを見ると、Efront 3.5.4以前の脆弱性とあります。明らかに該当しないので調べてみると、CVE-2008-3660の間違いのようですね。2008を2009とTYPOしたのでしょう。
 脆弱性の中味ですが、元記事に以下のように書かれています。

この脆弱性は不正なパスを指定することにより簡単にCGIバイナリをクラッシュしてしまう脆弱性です。

http://gihyo.jp/dev/serial/01/legacy-php/0004

 ちょっと欲求不満の残る書き方ですね。「不正なパス」とはなんでしょうか。NVDのサイトを見ると以下のように説明されています。

PHP 4.4.x before 4.4.9, and 5.x through 5.2.6, when used as a FastCGI module, allows remote attackers to cause a denial of service (crash) via a request with multiple dots preceding the extension, as demonstrated using foo..php.

http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2008-3660

 太字で示したように、foo..phpのように複数のドットを持ったファイル名があると、PHPがクラッシュするようです。
 なお、NVDの上記説明では、PHP4.4.9より前となっていますが、実際にはPHP4.4.9も該当するようです(大垣さんの記事の方が正しい)。
 アプリケーションに処理が渡る前にPHPがクラッシュしてしまうので、対策はアプリケーションの外側で行う必要があります。以下のいずれかによる対策が考えられます。

 元記事はmod_rewriteによる対策を紹介しています。

パッチを適用せずにクラッシュを回避するには,簡易なものでもよいのでWAFを導入します。簡易なWAFとはApachemod_rewriteを利用したプロキシをWAFとするなどの方法があります。

http://gihyo.jp/dev/serial/01/legacy-php/0004

 これはちょっと分かりにくいですね。NVDの説明にあるように、複数のドットがあるパス名が問題なので、このようなパスに対するmod_rewriteのルールを記述して、エラーページを表示する方法が考えられます。

x86 FPUクラッシュ

 この項はCVE識別番号が抜けています。私は最初、どのCVEに該当するのか探せなかったのですが、おそらくCVE-2010-4645(CVENVD)ですね。特定の数値を浮動小数点数に変換すると無限ループになるというもので、PHP以外のJavaRubyでも同種の問題がありました。PHP5.3.5以降、PHP5.2.17では対処されています。
 PHP4.4.9(Windows版)にて以下が無限ループになることを確認しました。

<?php
  $a = "2.2250738585072011e-308";
  $b = (double)$a;
?>

 元記事の対策は以下の通り。

入力値をバリデーションしてクラッシュを防ぐことが考えられます。

http://gihyo.jp/dev/serial/01/legacy-php/0004

 確かにそうなのですが、どうバリデーションすればよいかは書いていません。おそらくこの連載は、PHP4セキュリティ保守サービスという有償サービスの宣伝という意味があるようで、肝心なことは書いていないのかもしれません。そこで、勝手に回避策を紹介しましょう。
 アプリケーションでこの問題を回避するためには、攻撃数値文字列を *浮動小数点数に変換する前に* チェックする必要があります。これは少々やっかいです。指数形式が必要ないのであれば、以下のような正規表現でバリデーションする方法が考えられます。

if (preg_match('/\A-?[0-9]+(\.[0-9]+)?\z/', $a) == 0) {
  die('数値形式が不正です');
}

 厳密には、これだけではダメです。以下のように、指数形式を用いないPoCも存在するからです。

$a = '0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072011';

 小数点以下、0が307個並びます。このようなケースもあるので、全体の桁数を適当に制限するとよいでしょう。

exifのクラッシュ

 記事にあるように、PHP5.3.5以前に存在したCVE-2011-0708です(CVENVD)。すなわち、PHPの最新版5.3.6で修正されたばかりということになります。PHP5.2では修正されていません。
 ただし、64ビット環境でのみ脆弱となるようです。CVE、NVDにも明記されていますが、日本語の方が読みやすいので、ここはJVNから引用しましょう。

64-bit プラットフォーム上で稼働している PHPExif 拡張モジュール内にある exif.c は、不正な形式のキャストを処理してしまうため、サービス運用妨害 (DoS) 状態となる脆弱性が存在します。

http://jvndb.jvn.jp/ja/contents/2011/JVNDB-2011-001389.html

 結局このぜい弱性の影響を受けるのは、以下の全てを満たす場合と思われます。

  • PHP5.3.5以前(現時点の最新版5.3.6以外すべて)
  • PHPの64ビット版を使用している
  • exif_read_data()により、外部からアップロードされた画像を処理している

 従って、PHP4.4.9の32ビット版を使っている環境では、このぜい弱性は対処しなくて良いでしょう。仮に、上の全てに該当する場合は、exif_read_data()による処理を別の関数などに書き換えることで対策します。
 なお、Exploit Databaseには、ぜい弱性の条件が以下のように記述されています。

Using the following configuration, a system is most likely vulnerable:
(a) PHP 64bit version
(b) PHP compiled with --enable-exif
(c) memory_limit = -1

http://www.exploit-db.com/exploits/16261/

 このため、memory_limitを必要最小限の値にする(-1は無制限を意味する)ことで、保険的な対策になるかもしれません。ただし、私はこの点については未検証です。memory_limitを必要最小限にすることはDoS耐性を高める一般的な対策となるので、exifを使ってない場合でも、制限しておくと良いでしょう。

shmopの不正メモリ参照

 これもCVE識別番号が書いてありません。おそらくCVE-2011-1092だと思われます(CVENVD)。shmop_read()関数の整数オーバーフローの脆弱性ですね。PHP5.3.6(現時点の最新版)で修正されたものなので、レガシーPHPに限らず、相当のPHP記述のサイトが対象になります。これもPHP5.2では対策される見込みがありません。
 大垣さんは、この脆弱性を結構重く見ているようですが、私はそうは思いません。その理由は以下の通りです。

  • リモートから脆弱性を悪用されるシナリオが想定しにくい
  • ローカルの攻撃の場合でも権限昇格が起きるわけではない

 Redhatのレポートの末尾には以下のようなコメントがあります。私はこのコメントに同意します。

Statement:

Red Hat does not consider this to be a security issue. Input passed to these functions should be under the full control of the script author, thus no trust boundary is crossed. Additionally, an administrator would have to disable, or excessively increase the memory_limit settings in the PHP configuration file to trigger this bug.

https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2011-1092

 Expolitとしては以下が公表されています。

01	<?php
02	# Exploit Title: PHP <=5.3.5 Integer Overflow DoS
03	# Date: 12-03-11
04	# Author: Jose Carlos Norte - www.rooibo.com
05	# Software Link: www.php.net
06	# Version: <= 5.3.5
07	# Tested on: Ubuntu Linux
08	# CVE : CVE-2011-1092
09	 
10	$shm_key = ftok(__FILE__, 't');
11	$shm_id = shmop_open($shm_key, "c", 0644, 100);
12	$shm_data = shmop_read($shm_id, 1, 2147483647);
13	//if there is no segmentation fault past this point, we have 2gb of memory!
14	//or we are in a patched php
15	echo "this php version is not vulnerable!";
16	 
17	?>

 shmop_read関数の第3数に2147483647(32ビット符号付き整数の最大数)が指定されています。このように、非常に大きな値をshmop_read関数の第3引数に指定する必要がありますが、そのような状況がリモートから可能になることは、あまりないと考えられます。
 一方、ローカルからの攻撃については、権限昇格を伴わないと思われるので、攻撃の動機がありません。
 対策については、shmopを使っていないのであれば、元記事の回避策が有効です。

shmopモジュールをロードしない。shmop関数をdisable_functionで指定する。

http://gihyo.jp/dev/serial/01/legacy-php/0004

 もしもshmopを使っている場合は、shmop_read関数の使用状況を調べ、万一外部から第3引数をコントロールできる場合は、その設計を見直すか、第3引数の範囲チェックを行います。

[PR]

みずほダイレクトの謎

 ソフトバンク携帯電話のSSL方式変更に伴い、みずほ銀行のモバイルバンキングが一部使えなくなっていましたが、昨日復旧したようです。
 7月3日時点では、みずほダイレクトのトップに以下のように表示されていました。URLは魚拓のものです。

現在、ソフトバンクの携帯電話からアクセスいただいた、みずほダイレクト(モバイルバンキング)の[ネット決済振込サービス]および[Pay-easyペイジー)税金・料金払込みサービス]において、一部のお取引がご利用いただけません。「ご利用の端末のユーザーIDを「ON」に設定し、再度アクセスしてください。」と表示された場合には、パソコンなどでご利用ください。
お客さまには大変ご迷惑をおかけしておりますことをお詫び申しあげます。
(原因につきましては現在調査中です。)

http://megalodon.jp/2011-0703-0032-51/www.mizuhobank.co.jp/direct/index.html

 「ご利用の端末のユーザーIDを「ON」に設定し、再度アクセスしてください。」というエラーメッセージから、私は以下のように推測していました。

  • みずほダイレクトはSSLで運用している
  • ソフトバンク・ケータイのユーザIDを用いた「かんたんログイン」に対応している
  • secure.softbank.ne.jpの廃止に伴い、SSLではユーザIDを取得できなくなったのでシステムが動かなくなった

 このため、次のように対応すると予想していました。ソフトバンク端末の場合にかんたんログインをやめるか、ユーザIDではなく端末製造番号によるかんたんログインに切り替えるか、です。後者は好ましくありませんが、やむを得ずそうするかもしれないと思っていました。
 ところが昨日、いくつかの銀行のモバイルバンキングのURLを確認すると、多くの銀行がsecure.softbank.ne.jp経由のママになっていました(昨日の日記参照)。みずほ銀行も、secure.softbank.ne.jp経由のままです。
 secure.softbank.ne.jpは完全になくなるのではなく、公式サイトの一部(ホワイトリスト)で利用が続くとアナウンスされていましたので、そのこと自体はおかしくありませんが、モバイルバンキングのサイトがそれだったわけですね。
 しかし、ここで1つなぞが産まれました。secure.softbank.ne.jp経由が継続されたのに、どうしてトラブルになったのでしょうか。

 そして、昨日夕方には、みずほ銀行のトラブルは解消されたようです。アナウンスを引用します。

6月30日未明より、ソフトバンクにおけるネットワークの仕様変更にともない、ソフトバンクの携帯電話からアクセスいただいた、みずほダイレクト(モバイルバンキング)の[ネット振込決済サービス]および[Pay-easyペイジー)税金・料金払込みサービス]について、一部のお取引がご利用いただけない事象が発生しておりましたが、現在ご利用可能となっております。

http://www.mizuhobank.co.jp/direct/info/info110705.html

 関係者の皆様はお疲れ様でした。しかし、次に気になる文言が。

また、みずほダイレクト(モバイルバンキング)へのアクセス時に次のメッセージが表示される可能性がありますが、内容につきましては以下をご参照ください。

「このサイトは安全でない可能性があります。よろしいですか?」
→「はい」を選択してお進みください。「いいえ」を選択した場合はサイトへのアクセスが中断されます。
「はい」を選択後、遷移先のサイトのURLをご確認いただき、みずほ銀行のサイトであることをご確認のうえ、みずほダイレクト(モバイルバンキング)のログインを行ってください。
下記のみずほ銀行のURLと異なる表示がされた場合は、お手数ですが[お問い合わせ先] までご連絡ください。
[みずほ銀行のURL]
https://secure.softbank.ne.jp/P221-XXX.jskypl.jp-t.ne.jp
https://p221-XXX.jskypl.jp-t.ne.jp
https://P221-XXX.jskypl.jp-t.ne.jp
(XXXの部分には116〜120のいずれかが表示されます)

http://www.mizuhobank.co.jp/direct/info/info110705.html

 赤字にした部分は、今時それはないだろうと思うわけですが…
 では、やってみようと思い、手元のソフトバンク端末を用いてみずほ銀行のログイン画面を見てみました。Yahoo!ケータイの公式メニューから順にたどってみずほダイレクトのページに進みます。私は口座がないので、入口までです。
 すると、「このサイトは…」というエラーメッセージは出ません。URLを確認すると、「https://secure.softbank.ne.jp/P221…」になっていました。
 ここで、画面からURLを変更し、「secure.softbank.ne.jp/」を削除してEnd-to-EndのSSLにすると、今度は「このサイトは安全でない可能性があります。よろしいですか?」というエラーメッセージが。
 まさかオレオレ証明書を使っているわけもないしと思い、証明書を表示させると、正規の証明書です。エラーの原因はソフトバンクの技術情報ページを見て理由が分かりました。みずほ銀行の使っている証明書は「Verisign Class 3 Primary CA - G3」というタイプですが、これはソフトバンク端末の一部でエラー表示になることが明記されていました。
 つまり、こうでしょう。従来はsecure.softbank.ne.jp経由だったので、secure.softbank.ne.jpが認識する証明書が使えたわけで、「Verisign Class 3 Primary CA - G3」はその1つだったのでしょう。しかし、End-to-EndのSSLに変更すると、この証明書は端末側で認識できなかったのでしょう。

かんたんログインはどうか

 次に、かんたんログインについても調べてみました。
 まず、End-to-Endの接続のまま、かんたんログイン設定の画面に遷移し、契約者番号と暗証番号を空のまま登録ボタンを押してみました。すると、ユーザIDをONにせよというエラーメッセージになります。
 私はここで混乱しました。End-to-EndのSSLではユーザIDは送信されません。このIDはゲートウェイが付与しており、End-to-EndのSSLではゲートウェイはIDの付与(一種の改ざんになる)はできないからです。しかし、エラーメッセージと挙動から判断するに、事前の予想「端末製造番号でかんたんログインを実装」は外れたようです。
 そこで、secure.softbank.ne.jp経由のURLに戻して同じことをすると、今度は契約者番号と暗証番号が間違っている旨のエラーに変わります。やはり、ユーザIDを見ているようです。
 実験はここまでなのですが、以下のような謎が残ります。

  • 「このサイトは安全でない可能性があります。よろしいですか?」が表示されるのはどのようなケースか
  • 上に関連して、End-to-EndのSSLが画面遷移上選択されるケースはあるのか
  • End-to-EndのSSLにおいてはユーザIDは使えないが、そのようなケースは想定されているのか
  • 今までトラブルになっていた原因はなんだったのか

 そして、謎ではありませんが、やはり以下は言っておかなければ。

  • 『「このサイトは安全でない可能性があります。よろしいですか?」→「はい」を選択してお進みください』は、やっぱりまずい

追記1

 正規の証明書を使っているといっても、証明書のエラーメッセージが表示されても良いというわけではありません。本当に正規の証明書を使っているかどうかは、証明書のエラーが表示されないということでしか分からないわけで、その意味で、証明書のエラーメッセージを無視させることはとてもまずいと思います。
 証明書のエラーを無視させるくらいであれば、SSLの利用をやめてしまえばよいのにと思いました。携帯電話の場合、通信路の安全性は比較的高いので、考えられるリスクというのは、モバイルバンキングサイトの偽物を利用させられることです。SSLはそのために役に立つわけですが、この場合は、

  • ケータイの場合アドレスバーがないのでドメインの確認が難しい
  • 頑張ってドメインを確認しても、みずほ銀行であることがとても確認しにくいドメイン名になっている
  • 証明書のエラーを無視させているので、SSLがあてにならない

という理由から、にせものをつかまされるリスクは大きいと思います。みずほ銀行さんは、フィッシングサイトのようなウィルスに関するリリースを出しているわけですから、この問題はもっと切実にとらえる必要があります。
 このため、にせサイトをつかまされない方法をもっと大々的に告知すべきでしょう。一例として、利用者に、公式メニュー→オンラインバンキング→都市銀行みずほ銀行、という安全な経路で遷移し、そのトップページを携帯電話にブックマークしてもらう方法があります。加えて、メールに記載されたURL経由で、サイトを絶対に利用してはならないと注意喚起するべきです。

追記2

 これはみずほ銀行に限らないことですが、secure.softbank.ne.jpを今でも利用しているサイトは、早々に自社ドメインに引っ越すべきだと考えます。現在、攻撃者がsecure.softbank.ne.jp上に罠サイトを設けることはできなくなりましたが、secure.softbank.ne.jpを利用しているサイトに一箇所でもクロスサイト・スクリプティング(XSS)脆弱性があれば、secure.softbank.ne.jpを利用しているサイト全体が影響を受けます。XSS脆弱性の影響範囲は、ドメイン全体だからです。詳細は[PR]に載せた拙著を参照下さい。
 つまり、secure.softbank.ne.jpを使っている限り、サイトの安全性は自社だけでは担保できないことになります。自社がしっかりやっていても、secure.softbank.ne.jpを使っている他社サイトに脆弱性があれば、その影響を受けるのです。担当者の皆様、これを知って不安になりませんか?


[PR]

モバイルバンキングは今でもsecure.softbank.ne.jp方式が主流

 6月30日のsecure.softbank.ne.jp廃止*1にともない、みずほダイレクトにおいて、ソフトバンクの携帯電話からログインできなくなるという障害が報告されています(リリース)。
 他の銀行はどうかと思い、軽く調べて始めたところ、モバイルバンキングでは今でもsecure.softbank.ne.jp方式が堂々たる主流であることが分かりましたので報告します。

secure.softbank.ne.jpを経由する銀行

 以下は、ログイン画面のURLがhttps://secure.softbank.ne.jp/〜となっています。大手都市銀行は全てという感じです。

 secure.softbank.ne.jpはホワイトリスト方式で残されるということでしたが、上記モバイルバンキングは、そのホワイトリストの対象なのでしょう。

End-to-EndのSSLによる銀行

 以下は、ゲートウェイを経由しないEnd-to-EndのSSLです。

SSLを使っていない銀行

 以下はログイン画面でもSSLではありません。おそらく、ゲートウェイとWebサーバー専用線接続だと思われます。

 上記は、ソフトバンク携帯電話から各モバイルバンキングのログイン画面に遷移し、その画面のURLを調べる方法で調査しました。
 みずほ銀行がsecure.softbank.ne.jp方式を残したのにトラブっている原因は謎です。予想外の展開に驚きました。

私はいかにしてソフトバンク端末60機種のJavaScriptを検証したか

 昨日のソフトバンクの非公式JavaScript対応の調査結果 | 徳丸浩の日記で報告したように、昨年5月に、ソフトバンク60機種の検証を行い、JavaScript対応の状況などを調査しました。当時はまだ公式なJavaScript対応機種はない状態でしたが、既にほとんどの端末が *非公式に* JavaScriptに対応していました。
 このエントリでは、検証の様子を報告します。

なぜJavaScript対応状況を調査したか

 http://www.hash-c.co.jp/info/20091124.htmlを公表した前後に、とある方(この方)から、ソフトバンクのケータイでもJavaScriptが動作すると伺いました(参考のやりとり)。XMLHttpRequestも含めてJavaScrptが動くと教えていただいた932SHを私も購入して調べたところ、以下が判明しました。

 困りました。回避策がないので公表ができません。そのため、ツテをたどってソフトバンクモバイルのセキュリティ担当者に連絡をとっていただき、12月初旬に打合せを持ちました。打合せ自体は紳士的かつスムーズに進み、対応の検討をいただくことになりました。帰り際に、当面この件は秘密にするがあまりに対応に時間がかかるようであれば公表するかもしれないと申し上げました。
 その後約半年後に、WASForum2010にてドコモ端末でのDNSバインディング攻撃について発表することになりましたが、ソフトバンク端末にももっと深刻な問題があることを伏せて、ドコモ端末の問題のみを取り上げるのも心苦しい気がしました。
 そこで、あらためてソフトバンク端末でのDNSバインディング攻撃の影響について評価し直し、影響が軽微であると評価できれば公表することにしました。

まずはマニュアルの調査

 最近の携帯電話のマニュアルはPDF等で公開されており、ソフトバンク端末のマニュアルも公開されています。そこで、約100種類の端末についてマニュアル*1を調査しました。調査のポイントは以下の通りです。

 その結果、以下のことが分かりました。

  • シャープ製の端末はXMLHttpRequestに対応しているがデフォルトでは無効になっているものが多い
  • シャープ以外の端末はXMLHttpRequestに関する記述がなく、機能自体がないようにも思えるが、ないと断言はできない

 XMLHttpRequestがデフォルトで無効になっている端末だけであれば、NTTドコモの端末向けの対策で対処できるので新たな影響はないと考えたのですが、マニュアルの記述だけでは、全ての端末でXMLHttpRequestがデフォルト状態で無効になっているかどうかは分かりませんでした。

実機での検証を決意

 マニュアルだけからでは確証が得られなかったので、実機を用いて検証することを決意しました。携帯端末の実機検証用に、全ての携帯端末を保有していて貸し出しをする会社が日本には何社かあります。そのうちの一社に申し込みをして、実機での検証をすることにしました。
 実機検証の方法には何種類かありますが、一番安上がりの方法として、検証センターの会議室を借りて自分で検証する方法を選びました。その場合、端末の台数と、検証の時間で費用が変わります。
 端末の台数については、マニュアルによる調査の感触から、2006年春モデルから2010年春モデルまで、数機種ずつを選択することにして、60台くらいで傾向がつかめると予測しました。また、それ以前の海外製の端末もJavaScriptが動作するものがあると分かっていましたので、検証に加えることにしました。
 検証に要する時間は、1台あたり3分以下で終わらせることとして、60台で3時間という目標にしました。これは、かなり *忙しい* 想定です。そのため、以下に述べるように、時間短縮の工夫をしました。

検証用スクリプトの作成

 短時間で検証が終えられるように、検証用スクリプトを工夫しました。下図に、検証スクリプトの実行例を示します。

 表示されているように、上から、ダンプファイル名、iframe利用可否、JavaScript利用可否、XMLHttpRequestの利用可否とHostヘッダの書き換えの有無、User-Agentなどが分かるようになっています。
 端末の上側に、インクで821Nと書いているのは、端末をB5サイズのホワイトボード上においてマーカーペンで機種名を書き、全体を写真に撮ることで記録の効率化を図った結果です。

検証用ドメインの取得

 検証を効率化するためには、検証用スクリプトのURL入力時間も馬鹿にならないと予想しました。私が既に持っているtokumaru.orgやhash-c.co.jpでは、まどろっこしい気がしました。そのため、検証用にドメインを取得することにしました。
 携帯端末で打ちやすいドメイン名は、テンキーからワンクリックで入力できる文字のみで構成され、かつ文字数の少ないドメインがベストです。具体的には、ドットとA, D, G, J, M, P, T, Wのみで構成されるドメインです。また、同じ文字が連続してもいけません(AAD.JPはダメ)。たまたま.JPドメインはこの条件に当てはまるので、.JPとして取得できるドメインの最小文字数である3文字で、かつ先に述べたアルファベットだけからなるドメインが残っているか調べたところ、幸いまだ数個は残っていました。そのうちの一個を取得しました。これで、ドメインは***.jpと6文字になり、6ストロークのキー入力で検証用スクリプトのURLを入力できることになりました。

検証実施

 準備ができたので、2010年5月某日の13時に検証センターに入りました。
 検証センターでは、いきなり60台の端末を貸してくれるわけではなく、四角いトレイに乗った8台程度ずつを預かり、検証が済めばそれを返して次の端末が乗ったトレイを受け取るという仕組みでした。写真を取っておけば良かったのですが、当方も焦っていてそのような余裕はありませんでした。
 検証は全体としては順調に進んだのですが、もっとも手こずったのは携帯電話の操作でした。古いノキア製端末などは、操作方法が独特なものがあり、「これはどうやって使うんだ?」と少しばかり悩みました…が、なんとか分かりました。
 検証結果は、下図の写真のように、紙のシートに記録していきました。ノートPCは一応持って行きました。このノートPCは現地でスクリプトを修正するなどの緊急対応用でしたが、幸いなことに出番はありませんでした。

 記録用写真の最後のタイムスタンプは15:56。なんとか3時間で検証を終えることができました。

検証結果の例

 下図に検証結果の写真を示します。機種は、先ほどの821Nです。

JavaScriptの設定画面 基本の検証画面 ヘッダ改変チェック(HTTP)
Hostヘッダ、Refererが改変できている。
ヘッダ改変(HTTPS)
User-Agent、端末製造番号、
UIDが改変されている

 821Nは、JavaScriptによるヘッダ改変の自由度がもっとも高い機種の1つです。
 ご覧のように、HTTPSではUser-AgentとUIDの改変ができています。User-Agentが変更できるということは、端末製造番号も変更できるということです。
 HTTPSの方が変更できる幅が広いと書くと、「HTTPSは改ざんできないはずなのにどうしてだ?」と疑問に思われる方が出てくると思います。しかし、HTTPSは通信路上での改変を防ぐもので、端末上でのヘッダの変更を妨げるものではありません。さらに、HTTPの場合はゲートウェイでヘッダのチェックや(正しい値への)変更が入っているのに対して、HTTPSの場合はゲートウェイでのチェックや変更ができません。ゲートウェイは端末とWebサーバーの途中にあるので、HTTPSの保護範囲であり、ゲートウェイは素通しになるのです*3
 現在、某銀行において、ソフトバンク端末でオンラインバンキングが使用できなくなっています。アナウンスを読む限り、SSLでケータイIDをチェックしていたようですが、現実には、SSLでケータイIDを認証等に用いることは非常に危険です。ケータイIDを認証に使うこと自体が好ましくないわけですが、仮に使うのであれば、SSLを避けなければなりません。

結果

 検証の結果、820N、821N、830CA、940SCの4機種*4で、デフォルトでXMLHttpRequestが使えることが分かりました。これらの端末では、DNSバインディング攻撃の影響を強く受けるわけですが、幸いこれら機種のシェアは低く、危険性の公開に踏み切った方がマクロで見れば安全性が高まると判断しました。私の発表の後、すぐソフトバンクからもJavaScript停止の案内が出ましたので、情報公開して良かったと思います。

まとめ

 ソフトバンク端末のJavaScript対応状況の一部始終を報告しました。PCと違って、実機を用いないと検証ができないのでやっかいですね。せめて、端末に関する技術情報がもっと開示されるとよいと思います。
 また、ケータイに関する脆弱性を知ってしまうと、取り扱いに窮する場合があります。このあたりの実状は、このエントリの他、高木浩光氏のエントリ高木浩光@自宅の日記 - SoftBankガラケーの致命的な脆弱性がようやく解消も参考になります。

[PR]

*1:容量は1.65Gバイトになります

*2:iframe要素でもDNSバインディング攻撃は可能ですが、Hostヘッダの書き換えができないのでiframe要素の方はよしとしました。iframeによるDNSバインディングの実例については、ケータイtwitter(twtr.jp)においてDNS Rebinding攻撃に対する脆弱性を発見・通報し、即座に修正された - 徳丸浩の日記(2010-02-22)参照。

*3:ソフトバンクのゲートウェイ型SSLの脆弱性を振り返る | 徳丸浩の日記で報告したゲートウェイSSLの場合は別ですが、6月30日に廃止されました

*4:その後831NもデフォルトでXMLHttpRequestが使えることが分かりました。831はかんたん携帯なので、完全にノーマークでした。