2009年3月27日金曜日

JETエンジンにおいてパイプ記号「|」は今でも「危険」なのか

補足

この記事は旧徳丸浩の日記からの転載です(元URLアーカイブはてなブックマーク)。
備忘のため転載いたしますが、この記事は2009年3月27日に公開されたもので、当時の徳丸の考えを示すものを、基本的に内容を変更せずにそのまま転載するものです。
補足終わり

SQLインジェクション対策の続きで、セキュア・プログラミング講座には、2種類の文字を不受理にせよと書いてある。「;」(セミコロン)と「|」(パイプ記号)だ。

このうち、セミコロンについては不受理にする必要はなく、はっきり間違いといってよいだろう。『3)セミコロン「;」の拒否』の項ではセミコロンを用いた攻撃例も出ているが、脆弱性の原因はセミコロンではなく、シングルクォートをエスケープしていないところにある。バインド機構を用いるか、シングルクォートをエスケープすれば、セミコロンを恐れる必要は全くない。それに、複文を用いた攻撃をもっとも受けやすいMS SQL Serverの場合は、セミコロンなしでも複文を記述できることは前述した。すなわち、原理的にも、現実的にもセミコロンの拒否は意味がない。

一方パイプ記号の方はどうだろうか。少し長いが該当箇所を引用する。
5) その他の特殊記号への対処(Microsoft Jetエンジン)
またMicrosoftのJetエンジンでは、次の文字も機能をもつ特殊記号として扱われる。
  |  VBAステートメント実行文字
Jetエンジンは,与えられたSQL文の文字列の中に「|...|」で囲まれた部分があると、それをVBA(アプリケーション用のVisual Basicサブセット言語)のステートメントとして解釈し実行する。
 これは、SQL 文の中の「'...'」で囲まれた文字列の中に書かれていても起こる。
 これを悪用すると外部からの任意のシステムコマンドの投入が可能となり、最悪の場合、システムが乗っ取られるおそれがある。
このパイプ記号「|」をエスケープする方法は提供されていないため、パイプ記号「|」が含まれている入力パラメータは受理しないようにする必要がある。
  | → 受理しない
Jetエンジンは、Microsoft Accessのデータベースエンジンであるが、直接Access を利用しているつもりがなくても、拡張子「.MDB」をもつデータベースファイルにアクセスする際に使われることになるので注意が必要である。
[SQL組み立て時の引数チェック(アーカイブ)より引用]
この内容は他の文書類ではあまり見かけない。また、私はACCESSおよびJetエンジンの実務での開発経験がなく、また脆弱性診断などでもお目に掛からないので、今までこの問題を検証しないできたのだが、念のため確認してみた。

この問題に言及している文書としては、2000年2月に塩月誠人氏が公開された「セキュリティ勧告 - NTサーバ上におけるJetセキュリティ問題」がある。
Windows NT上で稼動する、MS Accessデータベース(mdbファイル)にアクセスするようなサーバプログラムは、Jetセキュリティ問題(いわゆる Office ODBCドライバ問題)の影響を受ける可能性がある。このセキュリティ問題の影響を受けるサーバプログラムに対し、悪意を持ったユーザが不正な入力を行なうことにより、サーバマシン上で任意のコマンドが起動する危険性が生じる。
[セキュリティ勧告 - NTサーバ上におけるJetセキュリティ問題より引用]
脆弱なサンプルと検証用文字列は以下のようになっている。

脆弱なサンプル:
  set db=Server.CreateObject("ADODB.Connection")
  db.Open "btcustmr"
  sql="select * from Customers where City='" & word & "'"
  set rs=db.Execute(sql)>
検証用文字列:
  |shell("cmd /c echo aaa > c:\test.txt")|   (注: "|" は縦棒文字)
手元のWindows Server 2003を用いて、上記を検証してみたが再現しなかった。パイプ記号は特に不都合なく挿入も検索もできる。塩月氏のレポートから9年も経っているので、Microsoft社が対策したのだろうか。試しにAccess 2003を当該Windows Serverに導入してみたが、現象は変わらなかった(TechNet Plus サブスクリプションを使用)。また、Access 2000 および Access 2002 で、安全でない関数が実行されないように Jet 4.0 を構成する方法などを参考にレジストリをいじったりしたが、やはり現象は再現しない。

このように、現在の環境では「Jetセキュリティ問題」を再現するのは難しいようだが、過去このような問題があったことは確実なので、現在においても「いかなる環境でも絶対に安全」とは断言できない。このような状況下でセキュリティコンサルタントとしてどのようにアドバイスすべきだろうか。

私がJetエンジンを今まで無視してきた理由は、同時に多数が利用するWebアプリケーション構築にJetのようなファイル共有タイプのデータベースエンジンを使用するのは好ましくないと考えるからだ。その方向で技術資料を探してみると、ぴったりそのままの文書が見つかった。
しかし Access ODBC ドライバ、および OLE DB Provider for Jet は、Web システムなどの多くのユーザーからのアクセスによる同時実行や高負荷の対応はされておらず、また、終日稼動で運用されるような高い信頼性を要求されるサーバー アプリケーションで使用されることを考慮して設計されていません。そのため、この様なシステムの場合、弊社では IIS/ASP と共に Microsoft SQL Server、または Microsoft Desktop Engine (以下 MSDE) 等のセキュリティ、常時運用の可用性・信頼性、および拡張性を備えたデータベースの使用を推奨しています。
[IIS 上での Jet データベース エンジンの使用について より引用]
上記のようにMicrosoft自身がWebシステムでのJetの使用を推奨していないのだ。そして、以下の内容が続く。
しかし、Access ODBC ドライバはスレッド セーフではないため、複数のユーザーが同時に MDB ファイル (Access データベース) に要求を行うと、予期せぬ動作が引き起こされる場合があり、システムの安定性に影響を及ぼす可能性があります。そのため、安定性、パフォーマンス、およびスレッド プーリングに対する修正および機能強化を含んでいる OLE DB Provider for Jet の使用を弊社では推奨しています。
注意 : OLE DB Provider for Jet はスレッド セーフですが、Jet がスレッドセーフでないため、完全なマルチ スレッド環境を実装することはできません。そのため、OLE DB Provider for Jet を使用した場合でも、応答がなくなるなど予期せぬ動作が引き起こされる可能性があります。
[IIS 上での Jet データベース エンジンの使用について より引用]
色々書いてあるが、「Jet がスレッドセーフでない」という箇所が重要だ。このことから、Jetを使用したWebアプリケーションでは「別人問題」のように、他ユーザの情報が漏洩する可能性などが考えられる。

まとめよう。「Jetセキュリティ問題」は過去の問題とは言い切れないかもしれないが、少なくとも現在では非常にトリビア的な問題だ。しかも、JetをWebシステムでは使うべきでないとMicrosoft自身が明記しているのだ。であれば、セキュア・プログラミング講座で説明すべきことは、「パイプ記号を受理しない」ではなく、こうだろう。

  例えば、Jetエンジンを避ける

参考:WASForum Conference 2008講演資料「SQLインジェクション対策再考」

2009年6月16日追記

本エントリを書いた後、セキュアプログラミング講座の内容は大きく改訂されたようで、ここで指摘した問題はすべて解消されている。関係者の皆様、ありがとうございました。

IPAの新版「セキュア・プログラミング講座」がイマイチだ

補足

この記事は旧徳丸浩の日記からの転載です(元URLアーカイブはてなブックマーク)。
備忘のため転載いたしますが、この記事は2009年3月27日に公開されたもので、当時の徳丸の考えを示すものを、基本的に内容を変更せずにそのまま転載するものです。
補足終わり

2002年3月にIPAから公開されたセキュア・プログラミング講座は、その後2007年に新版が公開された。旧版は今から7年前のコンテンツがベースになっているので現在の目から見ると色々突っ込みどころがあるが、2002年という時期にこれだけのコンテンツを揃えたというのは立派な仕事だったと考えている。

一方、2007年の新版はどうか。部分的に見れば「開発工程と脆弱性対策」の関連について言及するなど意欲的な内容もあるが、全体としては物足りない。新版が出た当時も「例えば、PHPを避ける」という表現が話題になったくらいで、同じIPAから公開されている「安全なウェブサイトの作り方」に比べれば、あまり影響力がないように思える。

しかし、「安全なウェブサイトの作り方」の第一版が2006年1月に出ているのであるから、その後に「セキュア・プログラミング講座」の新版を公開するからには、「安全なウェブサイトの作り方」の内容を包含し、その詳細版という位置づけであって欲しいと思うのだが、そのような内容にはなっていないのだ。大は、脆弱性に対する考え方から、小は、脆弱性の呼び方に至るまで、まったく異なるコンテンツになっている。これでは、両方のコンテンツを利用するユーザは混乱するだろう。

SQLインジェクション対策はどうか

これ以降は、「セキュア・プログラミング講座」の中から、現在緊急の課題であるSQLインジェクション対策の内容アーカイブ)を吟味したい。その項立ては以下のようになっている。

(1) 入力値のチェック
(2) 特殊記号のエスケープ
(3) プリペアドステートメントの使用

これらはいずれもSQLインジェクション対策として機能するものだが、この(1)と(2)と(3)の関係は、ANDすなわち「全部やれ」なのだろうか、ORすなわち「いずれかをやれ」なのだろうか。これらは並列に並んでいるだけなので分からない。

一方、安全なウェブサイトの作り方の方は、以下のようになっている。
■ 根本的解決
1)-1 SQL 文の組み立てにバインド機構を使用する
【中略】
1)-2 バインド機構を利用できない場合は、SQL 文を構成する全ての変数に対しエスケープ処理を行う
解説 これは、根本的解決 1) のバインド機構を利用した実装ができない場合に実施すべき実装です。
これなら、バインド機構とエスケープ処理が「どちらか一方」であること、バインド機構を優先して検討すべきことがよく分かる。

実は旧版のセキュア・プログラミング講座の内容を読むと、先の謎が解ける。旧版の方では以下のようになっているのだ。
入力値チェックを徹底しよう
任意のSQL文を混入されないためには,入力値チェックを徹底する必要がある。たとえば,
【中略】
しかし人名などの漢字文字コードを扱う場合,本節で紹介した手法では正しい形式かどうかを判断するのは難しい。次節では正しい形式かどうかの判断が困難な入力値を扱う場合について触れる。
入力文字列はエスケープしよう
人名など任意文字を許可する入力文字列を扱う場合,これが任意のSQL文として機能しないようにエスケープする必要がある。
【中略】
以上,SQL文の組み立てにおいて,エスケープ処理の必要性およびその手法について説明した。実はもっと手軽で便利なバインドメカニズムがある。次節ではこれについて説明する。
バインドメカニズムを活用しよう
[SQL組み立て時の引数チェックより引用]
これなら一応分かる。すなわち、第一選択肢としては「入力値チェック」だが「漢字文字コード」の場合はエスケープ、そしてエスケープの簡易便利版として「バインドメカニズム」がある、ということだろう。「安全なウェブサイトの作り方」の方には入力値チェックは対策として示されていないので、両者の推奨内容および優先順位が異なることにはなるが、意図は一応伝わる。

しかるに、新版の方は意図すら伝わらないことから、「駄目な技術文書の見分け方」という意味でよくない。これでは新版は改悪ではないのか。

というわけで、「セキュア・プログラミング講座」に関してIPAにお願いしたいのは以下の3点だ。
  • 技術文書として読んで分かるようにして欲しい
  • 「安全なウェブサイトの作り方」と方法論を統一して欲しい
  • せめて用語だけでも統一して欲しい
続く: JETエンジンにおいてパイプ記号「|」は今でも「危険」なのか

2009年6月16日追記

本エントリを書いた後、セキュアプログラミング講座の内容は大きく改訂されたようで、ここで指摘した問題はすべて解消されている。関係者の皆様、ありがとうございました。

フォロワー

ブログ アーカイブ