[PR]小規模ECサイトに最適なWAF、SiteGuard Lite

徳丸浩の日記


2009年09月18日 論点の整理

_文字エンコーディングバリデーションは自動化が望ましい

私が9月14日に書いたブログエントリPHP以外では - 既にあたり前になりつつある文字エンコーディングバリデーションに対して、大垣靖男さんから名指しで「セキュリティ専門家でも間違える!文字エンコーディング問題は難しいのか?」というエントリを頂戴しましたので、それに回答する内容を書きたいと思います。

まずは論点の整理から始めます。

合意していると思われる内容

 まずは合意できていると思われる内容から書き始めたいと思います。以下の内容は、大垣さんと私で合意事項だと考えています。

  • 論点1.文字エンコーディングの問題によるセキュリティ上の脅威がある
  • 論点2.文字エンコーディングに起因するセキュリティ上の問題に対して、文字エンコーディングのバリデーションが有効である
  • 論点3.Webアプリケーションによっては文字エンコーディングのバリデーションが不十分なことが原因で脆弱性が混入する場合がある

続いて、議論がかみ合ってないものの、実は合意していると思われる内容が論点4です。

  • 論点4.文字エンコーディング問題はバリデーションだけでは十分ではなく、総合的な対策が必要である

補足します。大垣さんの文章でいえば、例えば以下の部分です

しかし、中身を読めば私の意図はシステム全般に対して文字エンコーディングを厳格に処理しなければならない、と主張している事は分かるはずです

[何故かあたり前にならない文字エンコーディングバリデーションより引用]

私の方は、先のエントリでは触れていないだけで、やはり総合的な対策が必要だと考えています。その内容は、少し前にITproの連載で取り扱っています。

第5回から第9回が文字コード問題に関する総論、第10回から第12回がバリデーションに関する各論という位置づけです。実はこの8回でも終わりではなくて、表示(XSS対策)や、SQL呼び出し(SQLインジェクション対策)のところでも触れなければいけないと思いますが、ともかく文字コード問題を重視していること、バリデーションだけでは十分でないと私も考えていることは、お分かりいただけると思います。

とくに、第6回の文字集合に関する指摘は、ひょっとすると私のオリジナルの主張かもしれないと考えています。これは、2008年10月に開催されたセキュリティ・イベントBlack Hat Japan 2008におけるネットエージェント長谷川陽介氏*1の「趣味と実益の文字コード攻撃」というプレゼンテーション(資料はこちら)の中で「多対一の変換」として紹介されているものですが、文字エンコーディングの問題というよりは、文字集合の問題と整理した方が体系的に理解しやすいと思ったからです。

論点の整理について、話を戻します。元のエントリの冒頭には以下のように書きました。

大垣靖男さんの日記「何故かあたり前にならない文字エンコーディングバリデーション」に端を発して、入力データなどの文字エンコーディングの妥当性チェックをどう行うかが議論になっています。

つまり、先のエントリは大垣さんの主張全体に対して賛成とか反対とか言っているのではなく、大垣さんのエントリに刺激されて何人かのブロガーが意見を述べていることを受けて議論を展開しているものです。つまり、文字コード問題はバリデーションだけでは不十分であるという総論には同意しますが、でもバリデーションも大事だよねという各論の方に(私も含めて)何人かのブロガーが反応したわけですね。

意見が一致しない論点は自動か・手動か

さて、大垣さんと私で主張が一致していないかもしれないと思うのは、以下の論点です。

  • 論点5.文字エンコーディングの問題はアプリケーションプログラマができるだけ意識しないでも安全性が担保される仕組みが望ましい(徳丸の主張)

これは、元のエントリでは例えば以下の部分が該当します。

あるべき論で言えば、id:ikepyonが書いているように、言語などで自動的に検証が働くべきだと考えます。文字エンコーディングの妥当性は、文脈に関係なく、自動的にチェックできることですから、わざわざアプリケーションでチェックを記述する必要性はないからです。

そして、大垣さんは以下のように書かれていますので、アプリケーションが意識してチェックすべきとお考えなのだと認識しています。

RoRの脆弱性に関連してRuby1.9では安全、と解説されていますが、それはRuby1.9は不正な文字エンコーディングを受け付けないからです。個人的には明示的にバリデーションコードで検出する方が良いと考えています。

[何故かあたり前にならない文字エンコーディングバリデーションより引用]

また、大垣さんの最新のエントリでは以下のように述べられています。

このブログを読んでいる方の多くはWebアプリの開発者だと思うのでWebアプリ限定しますが、Webシステムの中心にいるWebアプリの開発者が「セキュリティ問題の責任はフレームワークやDBMSなどにある」と考えていては安全なWebアプリの開発が難しくなるばかりです。

すべての開発者が「セキュリティ対策を行う部分=自分が作っている部分」を"基本的な心得"として実践してれば、今よりもっと安全なシステム作れると思いませんか?

[セキュリティ対策を行うべき部分 - 自分が作っている部分より引用]

これはすなわち、アプリケーションプログラマがもっと自覚を持って、積極的にセキュリティ対策を行うべきだという主張と理解しました。その延長として、文字エンコーディングの問題も、アプリケーションプログラマが自覚・理解し、アプリケーション側での対策をとるべきだという主張だと理解しています。誤解があればご指摘下さい。

大垣さんの主張は総論としては賛成なのですが、しかしどこかで、アプリケーションと基盤ソフトウェアの責任境界に線を引く必要があります。それは、以下のように三つに分類できると思います。

  • (a)明らかにアプリケーションが責任をもつべき項目
  • (b)責任境界のあいまいなグレーゾーン
  • (c)明らかに基盤ソフトウェア(言語、ミドルウェア、OSなど)が責任をもつべき項目

文字エンコーディングのバリデーションに関して言えば、大垣さんの主張は(a)だと理解しています。徳丸の主張は(c)です。

仮に(c)という立場に立つと、「文字エンコーディングのバリデーションは言語が担当すべき内容なのにPHPは(デフォルト設定では)してくれない。これは問題ではないか」という主張につながります。

(a)という立場に立つと、「PHPは文字エンコーディングのバリデーションに利用できる関数がある*2なので便利だ。しかも、php.iniの設定次第では、パリデータをプログラミングしなくても安全な設定が可能だ。なんて便利なんだ」という主張になるかもしれません。

そして、異なる考え方が混在しているということは、現状は文字エンコーディングのバリデーションは、実態としては(b)のグレーゾーンなのです。

文字エンコーディングバリデーションの自動化の方向性

私は、今後のWeb開発環境の進化の方向性として、文字エンコーディングのバリデーションは基盤ソフトウェア側の担当になる *べき* だと考えています。そして、かつてはそうではなかったものの、最近では、文字エンコーディングのバリデーションを言語側で担当するように変化してきているととらえています。

Microsoftの場合、レガシーASPの場合は何もしてくれませんが、ASP.NETは、壊れた文字エンコーディングの問題を *起こす方が難しい* くらいです。そして、私はそれが言語の進化の正しい方向性だと考えています。ASP.NETにおける文字エンコーディングに関しては、こちらをご覧下さい。

Javaの場合も内部のUTF-16に変換するという過程を伴うので、壊れた文字エンコーディングによる問題を引き起こすのは難しいのですが、例外として過去のJavaVMにてUTF-8の冗長表現を許容するという問題がありました。その問題による脆弱性のサンプルはこちらをご覧ください。しかし、この問題にしても、脆弱性を再現するために *かなりわざとらしい* プログラミングをしなければなりませんでした。つまり、ブラックリストによる文字チェックをした後で文字エンコーディングを変換しています。「こんなことふつーしねーよ」と思いながら書いたことを思い出しますが、しかし、その種の「ふつーしねー」ようなことによりTomcatのディレクトリトラバーサル(CVE-2008-2938)が発生してる*3ので、絶対ないとは言い切れません。その意味で、2008年12月にリリースされたJava SE6 Update11にてUTF-8の冗長表現が禁止されたことで安全性が増しました。JavaVMの最新版を使う限り、「わざとらしい書き方をしても壊れた文字エンコーディングによる問題を起こすのは難しい」状態と考えています。

Ruby1.9の場合はいいですね。大垣さん自身が「Ruby1.9は不正な文字エンコーディングを受け付けない」と書かれています。

Perl5.8に関しては、誤解を与えてしまったようです。大垣さんが引用された私のサンプルは「万一壊れたUTF-8が入ってしまった場合」の実験です。通常の処理は、以下のように「入口でdecode、出口でencode」です。これらは文字エンコーディング変換とは限らなくて、外部文字エンコーディングがUTF-8の場合(すなわち文字エンコーディング変換をしない場合)でも、decode/encodeは必須です。

#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use CGI;
use Encode 'decode', 'encode';

my $q = CGI->new;
my $p = decode('UTF-8', $q->param('p'));
my $ep = $q->escapeHTML($p);

print encode 'UTF-8', <<EOT;
Content-Type: text/html; charset=UTF-8

<html><body>
$ep
</body></html>
EOT

Perlに関して言えば、「壊れた文字エンコーディングによる問題を起こす方が難しい」とまでは言えません。decode/encodeを使わないで、言い換えればutf8フラグを立てないでプログラムを記述することも依然として可能だからです。しかし、decodeは文字エンコーディングのチェックや変換ではなく内部形式への変換という位置づけであること、Perl5.8以降の標準的な書き方としてはdecode/encodeが推奨されていることから、今後はこのような書き方が普及していくものと思われます。いずれにせよ無条件に安全になるわけではないかことから、私の先のエントリでは、以下のような前提を書かなければなりませんでした。

前提として、最新の処理系を使用すること、Perlに関しては、use utf8;によるモダンな書き方をすることは必須です。

これら四言語で文字エンコーディングのチェックを自動化できる背景には、id:rryu氏の指摘のように、これら言語はオブジェクト自体に文字エンコーディングの情報があるという事実も重要です。Javaと.NETの文字列は元々UTF-16固定ですし、Perlは5.8以降でutf8フラグが導入されました。Rubyは1.9にて文字列オブジェクト毎に文字エンコーディングを指定できるという凝った仕様になりました。一方、PHPの文字列には文字エンコーディングの情報は含まれないので、PHPのマルチバイト文字列を扱う関数には文字エンコーディングを指定するようになっています。

しかるに、PHPは、文字エンコーディングのバリデーション方法は複数用意されているものの、漫然とプログラムを書いたのでは文字エンコーディングはチェックされません。それは、大垣さん自身もブログなどで繰り返し指摘されていますし、技術評論社の「なぜPHPアプリにセキュリティホールが多いのか?」という連載の最新号「第27回 見過ごされているWebアプリケーションのバリデーションの欠陥」でも、「PHPに限った話ではない」という前提ではありますが、この話題を展開されている通りです。それに対して、私は、文字エンコーディングバリデーションは自動化が望ましいし、PHP以外の言語ではその方向に進化していると主張したかったわけです。これはあくまでも入口でのバリデーションに限った話です。バリデーションで対応できない場合の例として、ASP.NETとMS SQL Serverの組み合わせにより、文字集合の問題が原因でXSSが発生する可能性について、以前まっちゃ445目覚まし勉強会にてライトニングトークをいたしました。そのときの資料を公開しましたのでご覧下さい(スライド28以降です)。

文字エンコーディング問題は難しい。だからこそ自動化を

さて、大垣さんのブログタイトルの「セキュリティ専門家」はもちろん私のことを指しているのですが、いったんそれを忘れますと、「セキュリティ専門家でも間違える!文字エンコーディング問題は難しいのか?」という状況はまさにその通りだと思います。文字エンコーディングの問題は難しいと思います。先に触れたITproの連載を執筆するにあたり、私は、文字集合や文字エンコーディングの説明から始めなければならなかったのです。このことが、問題の難しさと、普及のなさを示しています。そして、そこで私が書いた内容をすべてのアプリケーションプログラマが理解しろというのも実は酷な話だと思います。アプリケーションプログラマが勉強すべき内容には、もっと優先度の高いものが他にあるだろうと思うからです

であれば、自動化できる部分はプラットフォーム側でどんどん自動化することが、進むべき正しい道だと思います。かつてC言語でアプリケーションを開発していたころは、プログラマ全員がバッファオーバーフローを気にしなければならなかったのに対して、JavaやPerlやPHPなどでアプリケーションを記述するようになった今、バッファオーバーフローを気にしなければならないエンジニアは限定できるようになりました。それと同じような状況が、文字エンコーディングのバリデーションについても起こって欲しいし、既にASP.NETなどではその望ましい状況になっていると私は考えます。

*1 氏の肩書きはイベント時の紹介に従っています

*2 言語によってはバリデーション専用の関数がないものも多い

*3 「ふつーしねーよ」という感想についてはここも参考になります

本日のリンク元
アンテナ
その他のリンク元


[PR]小規模ECサイトに最適なWAF、SiteGuard Lite

ockeghem(徳丸浩)の日記はこちら
HASHコンサルティング株式会社

最近の記事

最近のツッコミ

  1. 通りすがり (09-19)
  2. otsune (09-19)
  3. 通りすがり (09-18)
Google