大垣靖男氏はISO27000を理解していない

大垣靖男氏著『Webアプリセキュリティ対策入門 ~あなたのサイトは大丈夫?』には付録として「サンプルBBS」のソースコードが掲載されています。大垣さんによる「入力バリデーション」の実際を、見てみます。


そのために、Lib/bbs.class.phpで定義されているValidateInputs()関数の挙動をみることにします。

ログイン画面からユーザ名、パスワードを受けて、この関数は、正規表現で検査した結果の返り値を、V_RETURN に格納する *はず* です。ところがせっかく得られた返り値 V_RETURN を入力バリデーションの判定に利用せずに捨ててしまっていますので、結果的にはなんでもオーケーになっていて、入力バリデーションをしていないのと同じになってしまっています。

この後、入力バリデーションが機能しないまま、 Login($username,$password) のパラメータに引き渡しているようです。ここからは、さらに、Lib/auth.class.php の Login($username,$password) が呼ばれるようです。(認証ですね)

これでは、大垣さんの設計したアプリケーションの機能要件からみて、非常に不味い事態です。


さて、さきに見たように、入力バリデーション用の正規表現の返り値を捨ててしまっていたのでまずかった、であるならば、コードを修正して返り値を使ってあげたら大丈夫なのでしょうか。

実はこれでも駄目なのです。というのは、ユーザ名とパスワードとの入力バリデーション用の正規表現は、ソースコード上では、両方とも、 ^[a-z0-9]$ であって、量指定子が記述されていないのです。 これでは、例えば、 yohgaki と云う正当なはずのユーザ名に対し正規表現でエラー値が返ってきてしまいます。やはり、想定されるべきアプリケーションの機能要件を満たしません。これもまた不味い事態です。


ここまでをまとめますと、ソースコード上では、BBSのログインのための入力項目、ユーザ名とパスワードへの入力バリデーション用の正規表現に誤りがあり、本来は正当な入力であってもことごとくエラー判定するはずなのですが、さらに誤ってその返り値を捨ててしまっているので、逆になんでもオーケーとなり、正しい入力バリデーションに失敗しているのでした。

【注意を払わなければ、いっけん正しく動いているようにみえるかもしれませんが…】


ここで大垣さんがバグを作り込んでしまった状況について考えてみます。

まず、さすがに自らの書籍のサンプルですから、BBSを実機に設置してテストしたはずと考えます。その際には、ユーザ名とパスワードを適宜入力して、『動く動く♪』と評価なさったことでしょう。

しかし、アプリケーションの機能要件の検査として、入力バリデーションを通過しないはずのテストデータを用意してのチェックをすることを、サボっていたことが明白でしょう。

また、ソースレベルで、ごくごく簡単な(英数字の)正規表現に量指定子のないという、目視で気がつくレベルの単純な誤りに気がつかないはずも 【普通なら】 ありません。

大垣さんは、成果物のソースコードをじっくり見ていないのでしょう。

従って返り値を使っていないというミスも見逃しています。

これらのミスのうち、どれかひとつでも潰していれば、芋づる式に他のミスにも気がついたに相違ありません。

きちんとデバッグできていないようです。

書籍の付録に対して、うかつにもほどがあります。


人間は誤りをおかす存在です。そのことをこの拙記事で問題視してあげつらうつもりは毛頭ありません。

ミスは訂正すれば良いのですから。


しかしながら、問題にしたいことは別にあります。

サンプルBBSのソースの分析から判ることは、大垣さんがミスを発見しようと努めなかった形跡が見受けられることです。これは宜しくありません。

入力バリデーションの威力を強調する書籍の付録のサンプルにおいて入力バリデーションに失敗していますから書籍出版上でなんとも不味い、【手抜きの露呈】と云う重大なリスクを見逃していたことになります。


さて、ここからが本題です。

最近の大垣さんの雑誌への寄稿やblog、SNSでの苛烈な記事を拝見致しますと、ISO27000という標準に倣え、というメッセージが目立ちます。翻って改めてISO27000を調べますと、ISO27000ではベリフィケーションとバリデーションとを対にして武器となし、情報システムのマネジメントの各プロセスにおいてこの武器を使うことを要求しています。

大垣さんはBBS作成にあたり、この両者、ベリフィケーションとバリデーションとを怠ったと考えられます。


なお、ISO27000でのバリデーションは、大垣さんの云うところの入力バリデーションとは異なる概念です。似ているのは単語だけです。この拙記事をお読み頂いた皆さんには、ISO27000策定にあたって参考にされた、システム工学で云うところの、バリデーション(ベリフィケーションと対応します)について、是非とも、興味を持って頂ければと存じます。


大垣さんはISO27000で定義されているバリデーションを理解もせず実践面で軽視し、書籍やblog、SNSで誤ったアウトプットを量産しておいでのようです。大垣さんが入力バリデーションを偏重しながらも、入力バリデーションに失敗しているのも、システム工学で云うところのバリデーションを実践できていないからでしょう。


【つい先日も、文法エラーがあったり正規表現が間違ったりと多重に脱線した短いコードをblogで提示なさっておいででして、この人は、一生のあいだ、バグがあっても平気なままでいるのかと】


結論:大垣靖男さんはISO27000を実践できるほどには理解していないのです。


………以下追記

このBBSでは、どうやら以下の条件により、ログアウトが実際にはちゃんと呼ばれないことが分かりました。


(1)ログアウト処理が GET で呼ばれている

(2)キャッシュを無効化していない


このため、ログアウトのURLは二回目以降キャッシュが表示されるのみで、処理が呼ばれていないのでした。

上記の(1)(2)のもとでログアウトしますと、そのあとに、ブラウザのバックボタンで戻れば、ログアウト前のコンテンツ(例えばスレッド一覧など)が見れます。ブラウザの機能でリロードしてもです。 これではログアウトとしては適切ではなく、IPAへ報告ものの駄目なレベルでしょう。


脆弱性の有無を調べることをせずに仕上げたサンプル掲示板を、セキュリティ解説本にのせては不味いです。


きちんとISO27000が謳う、validation(妥当性確認)をしましょう。入力バリデーションにばかりに気をとられていてはいけません。


なお、最新版のISO27000では、大垣さんが愛してやまない input data validation は、外されました。大垣さんは知ってか知らずか旧版をもとにあちこちにISO27000に倣えとおっしゃいますが、ことinput data validation に関しては、もうISO27000を根拠に使わないで頂ければと存じます。


……追記の追記

BBSアプリケーションから以下が呼ばれています。


lib/session.lib.php
ini_set('session.cache_limiter', 'private'); // 戻るボタンを有効にする


これによりブラウザのキャッシュが有効になります。

以下はPHPのマニュアルからの引用で出力されるレスポンスヘッダです。


Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: private, max-age=(session.cache_expire ぶんだけ未来), pre-check=(session.cache_expire ぶんだけ未来)
Last-Modified: (セッションが最後に保存されたときのタイムスタンプ)

http://php.net/manual/ja/function.session-cache-limiter.php より引用


この結果、ログアウトへのリンククリックにて……賢明な諸氏には自明と存じます。




本記事への大垣さんの反応をみつけましたので以下に引用します。

コードの問題点には気付いてますが、直せないんですよね。本ですから。確か短い正規表現で良さそうなのをコピペしたような気がします。しかし、バグがあるから考え方が根本的に間違っている、というのは暴論では?

⇒本文を読んで頂ければわかるはずなのですがバグがあるからではなくバグを取り除く気がない点を批判しています。ISO27000で云うところのリスクアセスメントが出来ておらずリスクトリートメントが出来ていません。ISO27000を実践できるほどには理解できていません。

コードの問題点には気付いてますが、直せないんですよね。本ですから。確か短い正規表現で良さそうなのをコピペしたような気がします。

⇒サポート用ないし訂正用のサイトを用意しておけば宜しかったと思います。こんな簡単な正規表現をコピペしたのならば技術者としてどうかと思いますし人様のコピペならば、なおさらテストしなければならないことでしょう。

タイトルからしてもバグがあるのでISOを理解していない、という風に読めますが?サンプルコードのチェックは甘かったと思います、内容の方に気を取られていたと思うので。それとISOの内容を理解していない、というのは論理に飛躍があります。

⇒誤読ですね。 ミスがあったからではなく、慢性的にみずからのアウトプットのミスを探しだしていないことから、ISO27000を実践できるほどには理解できていないと言っているのです。

コードがマズい、テスト不足、というのは理解していています。実際あまりテストしたり、コードを読み直した記憶がないので。別の本でも「おい」という間違いもあります。自分ですべき事をやってないだろ?と言いたいのでしょう。これは仕方ない批判ですね。しかし、それでタイトル、つまり論旨、が論理的に正しいとはならないと思いますが?論理的に正しく書くなら「実践してない」でしょう(苦笑

⇒実践できるほどの理解に到達していませんね。本文にも書きました通りです。自分ですべき事をやってないだろ?とおわかりなのですから慢性的な検査さぼり、ISO27000が要求するところのバリデーションのさぼりをやめてください。私の日記に苦笑できる余裕はないはずですが。

input data validation 入力値検証が最新版のISO27000に書いてあると頑なに信じているのも幻想です。理解していらっしゃらないことの証左です。

JScriptにてsetTimeoutを不思議なかたちで呼び出す

昨日の続きです

@hasegawayosuke さんから示唆をうけました。
下記のコードでは不思議なことに、setTimeout関数を呼び出せます。但し、location.hash に何かセットしたときにそうなるように組んであります。

var dum,
sum = 0,
calc = function calc(x) {
var temp = x + 1;
return temp;
};

if (location.search > "?") {
dum = setTimeout('calc(sum)',2000);
} else {
dum = "";
}
clearTimeout(dum);
/* why? */
RuntimeObject("")["setTimeout"]("alert('wao!')",0500);

下記にテストを臨時に設けました。location.searchを与えたときのみif文にてsetTimeoutが使われて、if文終了後のRuntimeObject()が、setTimeoutを拾っているためか、'wao'とalertされまます。

どこがおもしろいのか

上の例では、setTimeoutが呼び出されているときに、RuntimeObject("")で作成されたオブジェクトには、setTimeoutがはいっているということになります。ところが、上のコードを若干改造して確かめてみてもらうとわかるのですが、このオブジェクトの中身をforループで拾い上げると、setTimeoutが、はいっていないようにみえるのです。setTimeoutは、for…inループによる列挙の対象外であり、RuntimeObject("")で作成されたオブジェクトにおいてDontEnum属性が付与されていると推理できます。

このことは、setTimeoutのかわりに、eval でも同じになっているようです。

さらに謎

(function(){
var s_tmp2 = setTimeout('',6000);
})();

var s_tmp2 = 0;
RuntimeObject("s*")["setTimeout"]("alert('wao!')",2000);

さらに謎です。よくわからないのですが、グローバルなスコープではないところで、setTimeoutを呼び出しているとします。このとき、グローバルな場所で、RuntimeObject("s*")で作成されたオブジェクトに、setTimeoutが格納されていることが、私には不思議でたまりません。

同様に、

(function(){
var add = new Function("x", "y", "return x + y");
})();

RuntimeObject("F*")["Function"]("return alert('uuummmm')")();

にてuuummmmがalertされます。

いたずらテスト

いたずら

明示的にwindowなる語句を使わないでwindowオブジェクトを取得する裏ワザ

var name = "alert(1)";
var hash = "eval";
var R = RuntimeObject;
for (i in R()){
S = R()[i];
(S[hash])?S[hash](name):0;
}
以上で"alert(1)" が window.eval される。JScript専用

あるいは、以下。マスクを使う。

var name = "alert(1)";
var hash = "eval";
for ( i in ( s = RuntimeObject("windo*") ) ){
s[i][hash](name)
}

要するに、for ( i in ( s = RuntimeObject("windo*") ) ){ s[i] /* window */}だ。

AVTokyo2011でLTしました

AVTokyo2011( http://ja.avtokyo.org/avtokyo2011 )においてゲストスピーカーとしてご招待をいただきました。身に余る光栄です。ありがとうございます。
若干スライドを使いましたのでアップロードさせていただきました。
スッカスカですが、よろしければどうぞ。

個人的な思い

上記事項は本当にブラウザの脆弱性と言い切ってよいのかどうか躊躇がある。 そもそも標準規格にそってきちんとしたHTMLを吐き出していれば、ブラウザ側もきちんと文書をパース可能であってXSS脆弱性の入り込む余地はないからだ。まじめに作ってあるウェブアプリならば気にしなくてよいのだ。サーバ側ででたらめなHTMLを吐き出しておいてブラウザサイドでXSS発生…本当にブラウザの脆弱性なのだろうか?
そうは言っても、やはりIEの不具合である。この脆弱性の発見当初、いろいろなブラウザを調べていたが、Firefox は日本語処理において根本から相当に練っていたらしく、最も優秀であろうと筆者は感じた。敬意を表したい。

JVN#51325625の変種について::IEにみられるクロスサイトスクリプティングの脆弱性

この文書はなにか

JVN#51325625 が示めすように、Internet Explorer には、EUC-JP で記述された特定の文字列の処理に問題があり、クロスサイトスクリプティング脆弱性が存在する。この脆弱性の変種として、EUC-JP のみならず ISO-2022-JP においても同様な脆弱性が存在していることを示す。

断り書き

個人的な調査によるものであり、全面的な解析を含んでいないため、情報に欠落している部分や、間違っている部分もあるだろう。本記事を読まれた方々は、あらためて検討しなおして頂きたい。

影響を受けるシステム

InternetExplorer 6 ないし 7

概略

Internet Explorer には、ISO-2022-JP のHTML文書として解釈された文書の解析にあたり、そのファイルに記述された特定の文字列の処理に問題があり、クロスサイトスクリプティング脆弱性が存在する。但し、正しいISO-2022-JP文字集合(RFC1468相当)に即した文書ファイルには本脆弱性は存在しない。

インパク

ユーザのウェブブラウザ上で任意のスクリプトを実行される可能性ほか、一般の XSS 脆弱性にともなう攻撃をこうむる可能性がある。

サーバ側対策

ISO-2022-JP の規格に即した文字集合のみブラウザサイドに送出する。非標準的拡張使用や、そもそも規格に合致するかどうかチェックせずに自由なバイト列を送出することなどを許容するウェブアプリケーションでは、ブラウザに対する攻撃の足場になってしまうだろう。
※例示すれば、漢字については、第一水準およびに第二水準に限っているかどうかが検査のひとつの目安になるだろう。
※第一水準第二水準の外の漢字については、数値文字参照で送出すればよい。ISO-2022-JPから切り替えてUTF-8を採用することなども有効な手だ。
※「過激なエスケープ」「英数字以外の文字はエスケープ」というアイデアの採用は簡明かもしれない。数値文字参照やscript要素の中身におけるユニコードエスケープなど。

ブラウザ側対策

根本的な回避策はない。IE6 や IE7 は使わないほうがよい。最新のIEに乗り換えるか他の最新のブラウザに乗り換えるべきだろう。それでもIE6やIE7を使用したければ、危険を軽減するための簡明な処置として[インターネット ゾーン]のセキュリティ レベルを『高』に設定するなどの対処が考えられよう。

PoC例

脆弱性を発見した当時の環境のものをサンプルとして以下に例示する。IE6 on Windows XP SP3 の環境であった。

  • ESC $ B [0xE0 0xA5] ESC ( B ⇒ [0x20 0x22]
  • ESC $ B [0xE0 0xBF] ESC ( B ⇒ [0x20 0x3C]
  • ESC $ B [0xE0 0xC1] ESC ( B ⇒ [0x20 0x3E]
上記のように、不正なコードが文字化けするので、HTMLのマークアップに介入することが可能である。上記以外にも使えるバイト列があるので、ブラックリストで拾う対処策は効果が出にくい。筆者は確認しなかったが、偽物のバックスラッシュなどを作成することも考えうる。いずれにせよ、クロスサイトスクリプティング攻撃の可能性がある。

うっかりしやすいシナリオをいくつか

既知の UTF-7 でのXSS攻撃のシナリオが、形を変えて使用できる可能性がある。title要素の中身などは要注意ポイントだ。HTTPレスポンスにてきちんとcharset指定を行っているか、あるいは、次善の策だがtitle要素の出現以前にmeta要素でしっかりとcharsetを明示しているかなど注意しておきたい。また、サーバサイドではShift_JISのつもりでHTMLを出力していたはずでも、社会工学的なテクニックなどを利用してISO-2022-JPでのエンコーディングに切り替えさせる攻撃も成立するかもしれない。(IE6やIE7では手動でメニューから切り替えが可能だ。)「ISO-2022-JPのHTMLなんて作っていない」からといって油断はできない。OutlookExpressもIEのエンジンを積んでいる。メールではISO-2022-JPが使われることがあるのでくれぐれも慎重に。文字化けを人為的にひきおこすルートがたくさんありそうだ。

留意事項

筆者の知る限りIE8においては、本脆弱性が対処された。さらに、別の構造でISO-2022-JPに関するXSS脆弱性が発生した。MS10-090 はそのためのパッチであった。( http://jvndb.jvn.jp/ja/contents/2010/JVNDB-2010-000065.html ) このパッチに不十分なところがあり一部のサイトでISO-2022-JPによるページの閲覧ができなくなったことは記憶に新しい。MS10-090のパッチもしくはそれ以前のパッチによって本脆弱性がIE6ないしIE7において解消されたかどうかについては筆者は情報を持っていない。なお、未確認情報であるがMicroSoft社は当初、IE8における本脆弱性の解消のためにXSSフィルターの拡充を考えていた形跡がある。その計画はなくなった模様だ。
また、「先行バイトの埋め込み( http://gihyo.jp/admin/serial/01/charcode/0006 )」によるXSS攻撃とは原理が異なるので、比較した上でよく吟味していただきたい。

お願い

情報をお持ちでしたら、是非記事を作成してください。 本記事からリンクさせていただきます。また、コメントなど頂ければ記事の修正をいたします。よろしくお願い申し上げます。

XSS例題(下書き中)

課題草案(第四版)

アリスのサーバにはtext/html(charsetはUTF-8)でサーブされるHTML4.01相当のHTML文書を吐き出すサービスがある。以下はそのHTMLの一部分なのだが、第三者であるイブは、一定の条件に従った文字を出力できることに気がついた。

<script type="text/javascript">
ここらへんにイブが何か書ける
</script>
文字について一文字ずつ投入して調べたところイブが出力できる文字は、以下の通りであった。
  • スペース
  • 改行
  • セミコロン
  • 三種類の括弧 ( ) [ ] { }
  • ローマ字
  • 数字
イブは試みに、alert(1) 他、いろいろ出力したのだが、エラー表示となり駄目であった。つまり、( や ) については、書きだせる場合とそうではない場合があり、どうやらWAFによって、特定のケースではエラーとなりハネラレルことにイブは気がついた。アリスが設定したWAFは、非常に珍しいタイプのものであった。イブが調べた限りでは、以下のような設定になっている。
  • 関数に引数を渡すための括弧である、( や ) はエラー
  • メソッドにおいても上記同様
  • 一般に、関数オブジェクトに引数を渡す記述に使われる ( や ) はハネラレルようだ
  • 一方、ステートメントの構文に使われる( や ) は使える ≪if 文など≫註参照。
  • function キーワードもハネラレて書き下せない
  • Functionやconstructor も書き下せない
  • setter や getter も書き下せない
さて、イブがごにょごにょしているそばで、あなたはアドバイスをしてあげた。ほどなくしてイブは、アリスのこのHTMLに、とある文字列を出力することなどを行い、alert(0)することに成功した。あなたのアドバイスは如何なるものであったか?
なお、筆者が想定している解はすでにjsbin.com上にアップしてあり、はてなダイアリーからa要素でリンクすることで回答を示す予定である。

小括弧()についての註

この仮想的なWAFは、構文解析をしていて、【例えば】以下のようなケースでの ( ) を容認する。この考え方の圏内であればさまざまな変種を使ってよい。

<script type="text/javascript">
if (condition) {
statements
} else {
statements
}
for (initialization; condition; update) {
statements
}
for (variable in object) {
if (filter) {
statements
}
}
while (condition) {
statements
}
do {
statements
} while (condition);
switch (expression) {
case expression:
statements
default:
statements
}
with (eee) {
statements
}
</script>

解答例(第四版)

Firefox 4.0 on Windows 7 で動作確認しました。実際には、Firefox 1.6 の頃、すでに動作してます。下記のリンクかをクリックしてください。但し、別窓や別タブでは(永久ループしないように)細工がしてありますので、alert(1)しません。また、直接URLをアドレスバーにいれても動作しません。window.nameにスクリプトの断片を仕込んでいるからです。


  • 解答例(第四版)


  • Standard ECMA-357 ECMAScript for XML (E4X) Specification をサポートしているブラウザでは、The for-each-in Statement が使えます。 このステートメントを利用して、location に name を放り込んでいるわけです。等号の代替物として使ってみようか、というサンプルでした。