PostgreSQL Conference 2012で講演します

PostgreSQL Conference 2012で講演する機会を頂きましたので報告します。

日時:2012年2月24日(金曜日) 14時30分〜17時20分(徳丸の出番は16:30〜17:00
場所:AP 品川 (東京都港区)
費用:3,500円(申し込みはこちら
講演タイトル:安全なSQLの呼び出し方

今回は、IPA安全なSQLの呼び出し方を題材として、SQLインジェクションの発生原理から、安全なSQLの利用方法を基礎から実際までを説明します。
あまりトリッキーな攻撃の方法はしないつもりで、基礎的な話が中心となります。安全なSQLの呼び出し方の作成経緯については、「今夜こそわかる安全なSQLの呼び出し方 〜 高木浩光氏に聞いてみた」を参照下さい。

さて、「安全なSQLの呼び出し方」は以下の五名による執筆となっています。

執筆者 徳丸 浩   永安 佑希允   相馬 基邦   勝海 直人
    高木 浩光 独立行政法人産業技術総合研究所

執筆者に肩書きがないのは、独立行政法人情報処理推進機構IPA)の職員ということです。安全なSQLの呼び出し方の執筆者の一人に、IPAの徳丸浩という人がいる、ということですね。一方、今回の講演は、HASHコンサルティング株式会社代表としての徳丸浩がしゃべります。
ややこしいですが、こうすることで、IPAとしての公式な立場ではなく、徳丸の個人としての意見を自由にしゃべることができます。具体的には、独立行政法人セミナーでは仕込みにくい*ネタ*を取り入れることもできるわけですね(これ、けっこう重要)。

昨年は、PHP ConferenceYAPC::Asia TOKYO(Perlの祭典)で講演する機会を頂きました。今年の講演はじめはPostgreSQL Conferenceということで、オープンソースソフトウェアのカンファレンスでしゃべらせていただけることを嬉しく思います。
参加者190名ということですので、お申し込みはお早めに。

[PR]
安全なWebアプリケーションの作り方DRMフリーのPDFによる電子版もあります。

今年読まれた人気記事トップ10+10

今年も残りわずかとなり、今年の○○トップ10というタイトルを目にする機会も増えました。徳丸のブログでも、トップ10を発表したいと思います。今年書いたブログ限定ではなく、今年もっとも読まれたブログエントリのトップ10です。

  1. Apache killerは危険〜Apache killerを評価する上での注意〜
  2. PHP5.3.7のcrypt関数のバグはこうして生まれた
  3. もし『よくわかるPHPの教科書』の著者が徳丸浩の『安全なWebアプリケーションの作り方』を読んだら
  4. SQLのエスケープ再考
  5. EZwebの2011年秋冬モデル以降の変更内容とセキュリティ上の注意点
  6. PHP5.3.7のcrypt関数に致命的な脆弱性(Bug #55439)
  7. 都道府県型JPドメインがCookieに及ぼす影響の調査
  8. モテるセキュ女子力を磨くための4つの心得「SQLインジェクションができない女をアピールせよ」等
  9. evernoteのテキストをevernote社の管理者にも見えないように暗号化する
  10. 私はいかにしてソフトバンク端末60機種のJavaScriptを検証したか

ページビュー数では、1.の「Apache killerは危険〜Apache killerを評価する上での注意〜」がぶっちぎりでした。Apache Killerへの関心の高さが伺われますね。
2位の、PHP5.3.7のcrypt関数バグも大きな話題となりました。この問題への態度を観察していると、PHPerよりも他のクラスタの方が騒いでいる印象を受けました。なんというか、「真のPHPerはこの程度の問題では動じないのだ」という無言のメッセージを感じた気がしました。現実問題として、crypt関数あんまり使われてないような気がしますしね。
4位には、「SQLエスケープ再考」がランクインしています。3年半前のエントリですが、継続して読んでいただいています。私にとっても思い入れの深いエントリです。
8位の「セキュ女子力」も多くの方に読んでいただきました。本格的なコピペを書いたのは実は2回目でして、1回目は「Webアプリ脆弱性オタがふつーのSEの彼女に脆弱性世界を軽く紹介(ry」なんですが、元ネタが増田で出してきたのまで真似して増田で発表したという行きがかり上今まで匿名にしていました。あれから三年も経つのですね。もうばらしてもいいでしょう。

ちなみに、11位〜20位は以下の通りです。

  1. PHPのイタい入門書を読んでAjaxのXSSについて検討した(1)
  2. ソフトバンクのゲートウェイ型SSLの脆弱性を振り返る
  3. 「SQLインジェクション対策」でGoogle検索して上位15記事を検証した
  4. ぼくがPDOを採用しなかったわけ(Shift_JISによるSQLインジェクション)
  5. CSRF対策のトークンをワンタイムにしたら意図に反して脆弱になった実装例
  6. SQLの暗黙の型変換はワナがいっぱい
  7. KDDIの新GWで「かんたんログイン」なりすましの危険性あり直ちに対策された
  8. PHP5.4のhtmlspecialcharsに非互換問題
  9. 「安全なWebアプリケーションの作り方」電子書籍版9月28日(水)販売開始します
  10. 僕が「ホワイトリスト」を採用しなかった訳

今年は、なんと言っても「「徳丸本」を完成させることができ、多くの方に読んでいただいたことに尽きると思います。多くの方に支えられて初めてできたことです。本当にありがとうございました。来年もよろしくお願いいたします。

[PR]
安全なWebアプリケーションの作り方DRMフリーのPDFによる電子版もあります。

大垣本を読んで「バリデーションはセキュリティ対策」について検討した

このエントリでは、セキュリティの観点から、バリデーション実装について検討します。大垣さんの本を読んで「大垣流バリデーション」について勉強した結果を報告します。

はじめに

大垣さんの記事「入力バリデーションはセキュリティ対策」では、「入力バリデーションはセキュリティ対策である」が力説されています。この記事はおそらくid:ajiyoshiさんのブログ記事「妥当性とは仕様の所作 - SQLインジェクション対策とバリデーション」を受けてのことだと思います。id:ajiyoshiさんのエントリでは、「妥当性検証は仕様の問題であってセキュリティ対策ではありません」と明言されています。私はid:ajiyoshiさんに近い考えを持っていますので、大垣さんの主張について、私なりに考えてみました。
記事を書くにあたり、徳丸の立場を明確にしておきたいと思います。

  • バリデーションの基準は仕様の問題
  • バリデーションは本来セキュリティ目的でするものではない
  • バリデーションにはセキュリティ上の効果はある
  • バリデーションについての徳丸の関心は、「基準は何で、何をするか」

以下、順に説明します。

バリデーションの基準は何か

バリデーションの問題を議論する上での最初の(そして最大の)問題は、「何を基準に(正として)バリデーションするか」です。セキュリティの教科書によっては、セキュリティの都合により入力値の制約を定め、その制約を元にバリデーションするように説明しているものがあります。その具体例については、コレコレを参照下さい。
id:ajiyoshiさんのブログでは、バリデーションの基準はアプリケーションの仕様だと明記されています。徳丸も同意で、私の本のP79には「入力値検証の基準はアプリケーション要件」という節があります。
大垣さんの本では、「入力値はポジティブセキュリティモデルを使用して確実に検証する(P171)」などとありますが、「ポジティブセキュリティモデルってなんだ?」と、よく分からないので、大垣さんとtwitterで会話してみました。togetterにまとめましたので参照下さい。結論としては、大垣さんも「バリデーションはアプリケーションの仕様(文書化されているかどうかは別として)に従う」と考えられているようです。
つまり、バリデーションの基準についてはid:ajiyoshiさんと大垣さんの考えは同じということになります。

バリデーションのセキュリティ上の効果

次に、バリデーションのセキュリティ上の効果について検討します。
id:ajiyoshiさんは、バリデーションのセキュリティ上の効果については言及されていないようです。これは実に潔い態度と言えますが、一般的には、バリデーションに一定のセキュリティ上の効果を認める人が多いと思います。このあたりの話題は、稿を改めて書きたいと思っています。
一方、大垣さんは、ご自身の本の中で以下のように書いておられます。

クロスサイトスクリプティングSQLインジェクション、HTTPレスポンススプリッテイングなど、入力さえ厳密に確認していればセキュリティホールとならずにすむ場合が数多くあります。

様々な脆弱性について防御上の効果があるが、常に攻撃を防げるわけではない、ということですね。これはセキュリティ観点から見たバリデーションの特徴です。
「さまざまな脅威に対して効果がある」という、いわば万能性に着目すると、「バリデーションはなんて素晴らしい『セキュリティ対策』なんだ」という評価になります。一方、常に防げるわけではない点に着目すると、「バリデーションで常に防御できるわけではないし、そもそも本質な対策ではない。そのようなものは『セキュリティ対策』とは認めない」という評価になるのでしょう。
つまり、バリデーションにどのような効果があるかについて見解が分かれているわけではなく、どの特徴に着目するかによって、バリデーションとは何かという表現が変わっていることです。「バリデーションはセキュリティ対策か否か」という問題の答えは「セキュリティ対策とは何か」という定義次第で変わるため、私はこの「セキュリティ対策か否か」という議論には興味がありません。

バリデーションの結果何をするのか

次に、バリデーションの結果「基準(=仕様)を満たさない」入力をどう扱うかについて考えます。
id:ajiyoshiさんのエントリには、『それ以外の入力については、「郵便番号を入力してください」というメッセージを表示して再入力を促す』などと例示されているので、入力画面に戻って再入力させるということですね。これは(曖昧な用語を敢えて使いますが)正常系の流れの一部とみなすことができ、「セキュリティ対策なんてもんじゃないよ」というid:ajiyoshiさんの主張もうなずけます。
一方、大垣さんの主張はどうでしょうか。先のエントリを読んでもよく分からなかったので、大垣さんの本から拾ってみました。同書のP168〜P169には、入力値の確認方法の「よい」例として以下のスクリプトが紹介されています。

if (is_number($_GET['int'])) {
  $n = $_GET['int'];
} else {
  trigger_error('数値ではありません。$n=', $n);
}
if ($min >= $n && $n >= $max) {
  trigger_error('数値が範囲外です。$n=', $n);
}

これを見ると、バリデーションの結果仕様外と判定された入力については、trigger_error関数を呼んでエラーとしています。trigger_error関数は、ユーザ定義のエラーを発生させる関数です。このスクリプトの後に、以下の説明が続きます。

trigger_errorで登録されたユーザーエラーハンドラで、無効なリクエストのエラー情報、送信したIP、ブラウザ、時間などを記録し、必要であればメールで通知したりアクセスを禁止しセッションを破棄するなどの処理を行えば、ほとんどのサイトでは十分でしょう。

かなり物々しいことが書いてありますね。このようなアプリケーションを利用することは、かなりの緊張感を伴いそうです。この前のところには、「人の年齢の場合は-10才などという数値はないはずですし、普通は0〜130くらいまでで十分でしょう」とあります。この文の内容には同意ですが、trigger_errorの処理内容とあわせて考えると、年齢を31と入れようとして手が滑って311と入力した場合でも、セッションが破棄され(結果としてログアウトし)、アクセスを遮断され、(管理者に?)メールで通知されるのでしょうか。
同書のAPPENDIX.Cには、P237からエラーハンドラの実装例が出ています。これを読むと、メール通信やアクセスの遮断はしていませんが、セッション破棄のコードはあります。

if (empty($_SESSION)) {
    $_SESSION = array();
}

empty関数は、変数が空の場合に真を返します。空とは、空文字列、0、NULL、FALSE、空の配列などを指します。このサンプルはプログラムの最初の方でsession_start()を呼んでいますので、$_SESSIONの値はなんらかの配列のはずです。すなわち、上のスクリプトは、$_SESSIONが空配列の場合に、あらためて空配列を代入していることになります。しかし、これは恐らくバグで、$_SESSIONが空でない場合に空にしたいのでしょう*1。以下、私の推定が正しいと仮定して議論を進めます。
エラーハンドラは、セッション破棄の他に、スタックトレース、$_GET、$_POST、$_SESSION、$_SERVERの全ての値をログに生成しています*2
そして、エラーハンドラの最後にexitでアプリケーションを明示的に終了しています。その前には、「//すべてのエラーは致命的なエラーとして処理」とコメントがついています。
これは中々タフなユーザー体験です。年齢や郵便番号などをうっかり間違えただけで、ログアウトしてエラー表示されるわけですから。複数のエラーがある場合、表示されるのは最初の1つのみです。このようなアプリケーションを使うと、かなり「残念な」思いをするように思います。一方、「なるほど、『セキュリティ対策』とはこれを指していたのか」という異様な説得力があります。
しかし、このような「セキュリティ対策」は本当に「世界の常識」なのでしょうか。Googleも、Facebookも、Amazonも「不正な入力」に対してそのような挙動は示さないようです。
現実にはそこまで厳しくしなくても、アプリケーションの安全性は保てると思います。バリデーションの結果、仕様外の入力があった場合は、間違いの理由を明示して再入力を促すのがよいと思います。「そんな対応ではセキュリティ対策とは言えない」ということであれば、バリデーションはセキュリティ対策でなくて結構と思いますし、それで安全性が損なわれるとも思えません。

まとめ

「入力バリデーションはセキュリティ対策」かという命題について検討しました。バリデーションの基準については、アプリケーションの仕様と言うことで一致しているようですが、仕様外の入力があった場合の挙動については、大垣本の教えは独特であることがわかりました*3
私自身は、バリデーションは「セキュリティ対策」の一種ととらえてもよい(どうでもいい)と思いますが、仕様外の入力に際しては利用者に分かりやすいメッセージを表示して、再入力を促すのがよいと考えます。

蛇足

先に引用した数値検証のスクリプトにはバグがあります。
※ このスクリプトは「間違い探し用のコードが手違いで収録されていました」と指摘されていますので、以下はその間違い探しの答えに当たりますね。

  • is_numberという関数はPHPには標準で用意されていない。is_numericという関数ならある*4
  • 「数値ではありません」の後の$nは初期化されていない変数を参照している
  • 範囲チェックのif文で等号があるのは間違い
  • 範囲チェックのif文は&&ではなく||にすべき

これらのバグの結果、実行時に未定義関数のエラーになりますが、is_numberをis_numericに置き換えて実行すると、$nの値に関わらず常にチェックを通ることになります。「数値が範囲外です」というエラーメッセージが表示されることはありません。

このスクリプトに続くのは「よい文字列確認」と題されたスクリプトです。以下に引用します。

if (strlen($_GET['name']) > 20) {
  trigger_error('名前が長すぎます');
} else if (strlen($_GET['name']) <= 1) {
  trigger_error('名前が短すぎます');
} else if (strlen($_GET['name']) != strspn('abcdefghijklmnopqrstuvwxyz')) {
  trigger_error('名前には小文字のアルファベットのみが使用できます');
}

ここにstrspnという関数が出てきます。以下の仕様です。

int strspn ( string $subject , string $mask [, int $start [, int $length ]] )
subject : 調べたい文字列。
mask : 許可する文字の一覧。
start : subject の中で調べ始める位置。
length : subject 内で調べる部分の長さ。

返り値 : subject の中で、全て mask の中の文字からなる最初のセグメントの長さを返します。

http://php.net/manual/ja/function.strspn.php

すなわち、第1引数の文字列の中から英小文字だけからなる部分を探しその長さを返すということなのですが、残念ながら第1引数('$_GET['name'])が抜けているので、実行時にエラーになります。これもバグですね。書きっぱなしで、テストはしなかったのでしょうね。
そもそも、strspnをバリデーションに用いるのはどうなのでしょうね。aからzを漏れなく入力しているか不安で仕方ありませんし、プログラムが読みにくく、なにか意図しない動作をしてしまいそうです。その懸念は、次のサンプルで現実のものとなります。
次のバリデーションサンプルは、入力文字列がSHA1ハッシュの要件(16進数40桁)を満たしていることを確認するものです。以下に主要部分のみ引用します。

// hex形式のsha1値は0-9, a-fまでの40文字
$error = (40 != strspn($str, '1234567890abcdef'));

40桁の16進数であることを確認したつもりなのでしょうが、このスクリプトだと、*先頭40文字が16進数* であることのチェックになります。要件を満たすには、文字列長が40であることも確認しなければなりません。
この結果、41文字目以降に攻撃文字列がある場合も、バリデーションをすり抜けます。以下に例を示します。

<?php
$str = "0123456789012345678901234567890123456789'or'a'='a";
$error = (40 != strspn($str, '1234567890abcdef'));
var_dump($error);
【実行結果】
bool(false)

$errorがfalse、すなわちエラーなしという結果になります。これはまずい。SQL呼び出し部分にSQLインジェクション脆弱性がある場合、任意のハッシュ値でチェック処理を通過できるなどの攻撃が考えられます。

脆弱性診断で上記が確認できた場合を考えましょう。SQLインジェクション脆弱性があればもちろん高危険度の脆弱性ですが、危険な脆弱性が発見されず、バリデーションの不備のみである場合、私なら低危険度、あるいは単なる指摘(Information)とします。
しかし、大垣さんのお考えですと、「入力バリデーションは非常に重要なセキュリティ対策の第一番目」ということですので、その「非常に重要なセキュリティ対策」に欠陥があるということは、上記は重大な脆弱性ということでしょうか。

この蛇足から導ける教訓は以下の通りです。

  • プログラムを書いたら必ずテストしよう
  • トリッキーな書き方は脆弱性の元なので平明な書き方にしよう

訂正(2011/12/26 8:36)

タイトルが「パリデーション」となっていたのでバリデーションに修正しました。本文も同様に2カ所修正しました。

[PR]
安全なWebアプリケーションの作り方DRMフリーのPDFによる電子版もあります。

*1:私が同じことをしたい場合は、if文なしに問答無用で空配列を代入してしまうと思います。

*2:もっとも、$_SESSIONはログデータを作る前に破棄しているので、意味がないように思えます。

*3:但し5年前に発行された本ですので、大垣さんの現在のお考えは変わっているかもしれません。

*4:is_numberをGoogle検索すると、「次の検索結果を表示しています: is_numeric」と表示されます

「徳丸本をブロガーに差し上げちゃうキャンペーン」当選者のお知らせ

徳丸本をブロガーに差し上げちゃうキャンペーン」ですが、先週末に選考を行い、当選者には個別に連絡させていただきました。当選表明のブログが出そろいましたので発表いたします。当選者は以下のブロガーの皆様です。

本日より順次書籍を発送致します。徳丸本は重い(約1kg)ので、3冊ずつ発送するつもりです。しばらくお待ち下さい。クリスマスには間に合うと思いますw

土日にいそいそと宛名書きをしていましたら、こんな指摘が

@ockeghem 今回のイベント、なんだかプレゼントの送り手が一番しあわせそうですね (^^)

https://twitter.com/#!/tt4cs/status/148325070501191680

おっと、これは図星でした。贈り物というのは、送り手が幸せになる為にするのですね。私の為に付き合って下さった皆様ありがとうございました。
選に漏れた方、ゴメンナサイ。徳丸の勘でテキトーに選んでおりますので、優劣を決めたものではないことを申し添えます。

それでは、当選者の皆様、感想のブログをお待ちしております。
読むのに時間が掛かると思いますので、焦って書く必要はありませんよ。

[PR]
安全なWebアプリケーションの作り方DRMフリーのPDFによる電子版もあります。

徳丸本をブロガーに差し上げちゃうキャンペーンのお知らせ

「徳丸本」こと「体系的に学ぶ 安全なWebアプリケーションの作り方」は、このたび増刷(第5刷)が決定いたしました。皆様のご愛読に深く感謝申し上げます。増刷を記念して、徳丸本を買いたくてもまだ買えていなかった方々に、徳丸本を進呈するキャンペーンを企画いたしました。

私の手元に徳丸本第3刷の在庫が数冊あります。元々献本用や(HASHコンサルティングの)営業活動の贈呈用として購入していたものですが、思ったほど「差し上げる」機会がなくて、死蔵されているような格好でした(この在庫とは別に増刷ごとに何冊かずつ送付いただいているため)。そんなタイミングで第5刷が出ることになったので、これはいかん、在庫をなんとかしなければと思いました。
とはいえ、著者である私が、Yahoo!オークションAmazonマーケットプレイスでこの在庫を販売することもためらわれました。
そこで、冒頭に書いたように、何冊かを進呈しようと思い立ちました。

キャンペーンの概要

以下の条件に該当する方で、応募いただいた中から、数名の方に徳丸本を進呈いたします。

必須条件(and条件)

  • 徳丸本を持っていない方
  • 継続してブログを書いておられる方
  • 当選後、感想などをブログに書いていただける方

できれば学生さんなど、収入の限られる方を応援したいと思っております。
また、感想は何でも率直な意見等を書いていただければ結構です。当方からは、特に制約はつけません。普段、他人様の書籍を酷評している立場ですので、自分の時だけ「ほめてね」とは、注文をつけるわけにいかないと思っております。
書籍費用と発送費用は徳丸が負担いたします。徳丸のサインをご希望の場合は、サインします。
また、前述のように、進呈する書籍は「徳丸本」第3刷です。その後の変更内容については、正誤表をご確認ください。

応募方法

以下のいずれかの方法で「徳丸本プレゼント応募」とご連絡ください。

  • twitter上でメンションいただく( @ockeghem )
  • メールする( 'or'1'='1'--@tokumaru.org )

いずれの場合でも、ブログのURLを明記ください。また、当方から受信確認の返信を必ずしますので、もし返信がない場合は、恐れ入りますが再確認をお願いいたします。

締め切りはとくに設けません。応募いただいた中から不定期に選考させていただき、当選された方に連絡します。発送に先立ち、ブログにて「当選した」旨を発表ください。それを確認後に、書籍を発送いたします。また、在庫のうち何冊を進呈するかは特に決めていません。
選考は、徳丸の独断と偏見で行いますので、あらかじめご了承ください。ブログの内容が高度であるとか、読者が多い必要はまったくありません。ただし、徳丸本の読者層に当てはまっているかどうかは見させていただく可能性があります(徳丸本は元々は初心者向けとして企画されました)。「応募した」旨のエントリにて決意のほどを表明いただければ、当選の可能性は高まると思います。

また、キャンペーンで不明なことがあれば、twitter上のメンションで質問してください。適宜、回答をこのエントリに追記したいと思います。
それでは、皆様のご応募をお待ちしております。

2011/12/17 15:00追記

キャンペーンの申し込みは締め切りました。18名の方にご応募頂きました。活発なご応募に感謝します。現在、当選者の方に連絡しております。

[PR]
安全なWebアプリケーションの作り方DRMフリーのPDFによる電子版もあります。

何が正しいのかを考える際は、正しさの基準が必要

 大垣さんの寄稿記事「第44回 セキュリティ対策が確実に実施されない2つの理由:なぜPHPアプリにセキュリティホールが多いのか?|gihyo.jp … 技術評論社」のまとめにて、『最後に「何が正しいのか?」常に考えるようにしてください』と書かれています。この部分は、私への反論のようですので、このエントリで返答したいと思います。

大垣さんの主張

 先にも述べたように、大垣さんはこのエントリの「まとめ」として以下のように書かれています。

最後に「何が正しいのか?」常に考えるようにしてください。

http://gihyo.jp/dev/serial/01/php-security/0044?page=2

 この主張自体には私も大賛成です。大垣さんの記事は以下のように続きます。

例えば,SQL文を作成する場合にリテラル(パラメータ)を文字列としてエスケープすると浮動小数点型のデータが正しく処理されないデータベースがあります。これは,SQLインジェクション対策の基本の1つである「すべてのリテラルを文字列として処理」することが間違っているのでしょうか? それとも「文字列として渡された浮動小数リテラルを正しく処理できない」処理系が間違っているのでしょうか?

http://gihyo.jp/dev/serial/01/php-security/0044?page=2

 私は、「(数値も含めて)すべてのリテラルを文字列として処理」に対して一貫して反対してきました。たとえば下記の記事です。そのため、大垣さんの記事は私への反論なのでしょう。

 1番目のエントリで私は、MySQLで文字列リテラルを数値に「暗黙の型変換」すると、結果は浮動小数点数になり、予期しない動作となる例を紹介しています。厳密に言うと、この動作と、大垣さんの指摘された「SQL文を作成する場合にリテラル(パラメータ)を文字列としてエスケープすると浮動小数点型のデータが正しく処理されないデータベースがあります」は、内容は異なります。私の主張は「浮動小数点数でないはずの数値まで浮動小数点数として扱われる」ことが問題だとしているからです。しかし、今は、細かい表現上の違いは気にしないことにしましょう。
 以下、大垣さんの主張の要点である「(数値も含めて)すべてのリテラルを文字列として処理」することの是非について、「何が正しいのか?」という視点から検討します。

議論の前提:数値を文字列リテラルとして扱うと「暗黙の型変換」が問題に

 大垣さんの主張、すなわち「(数値も含めて)すべてのリテラルを文字列として処理」すると、必然的に「暗黙の型変換」が出てきます。以下のSQL文を用いて説明します。

SELECT * FROM employee WHERE age = '30'

この例では、年齢を示す30を文字列リテラルとして与えています。数値列ageとの比較において、文字列'30'は「暗黙に」数値 30 に変換されることを期待したSQLです。私は、この「暗黙の型変換」が問題だと考えます。

正しいことの判断基準は何か

 ここで、「何が正しいのか」を検討するにあたり、「正しいとは何か」ということを考えてみたいと思います。正しいか、正しくないかを論じるためには、判断基準が必要です。
 現在議論しているのは、「SQL文を作成する場合」の話ですから、大前提として作成されたSQL文が正しくなければならないはずです。それでは、SQL文が正しいとはどういう状態でしょうか。
 これも色々な条件があるはずですが、正しいSQL文の基本条件の一つとして、SQL規格や実際に使うSQL処理系の文法に従っていることが必要と考えます。たとえば、PostgreSQLを使う場合は、以下が必要です。

  • 原則としてISO/JISの規格に従ったSQL文であること
  • その上で、使用するSQL処理系(この場合はPostgreSQL)の方言や制限にあわせるための調整をしていること

 この基準に従い、暗黙の型変換を伴うSQL文の「正しさ」について検討しましょう。

ISO/JIS規格のSQLでは、文字列から数値への暗黙の型変換は定義されていない

 ISO/JIS規格のSQLでは、文字列から数値への「暗黙の型変換」はどのように規定されているでしょうか。
 実は「規定されていない」が正解です。それをISO SQLのドキュメントから追ってみましょう。
 ISOのSQL規格には、SQL製品の規格準拠度を調べるためのチェックリストがあります。Annex F「SQL feature and package taxonomy」がそれです。PostgreSQLOracleなどSQL製品は、このリストへの準拠をチェックリストの形で公開しています。まず、以下は、Annex FのE011-06「数値データ型間の暗黙の型キャスト」の引用です。

 ISOの標準をお持ちの方は少ないと思いますが、幸いなことに、Annex Fは各SQL製品のリファレンスマニュアルに引用されています。以下に、PostgreSQL9.1の該当箇所を紹介します。

http://www.postgresql.jp/document/9.1/html/features-sql-standard.html

これを読むと、暗黙の型変換については以下の記述が見つかります。

  • E011-06 コア 数値データ型間の暗黙キャスト
  • E021-10 コア 文字列型間の暗黙的キャスト

 数値型同士、文字列型同士の「暗黙の型変換」については、ISO規格の要求があることが分かります。しかし、文字列型から数値データへの暗黙の型変換については、記載がありません。このことから、ISO SQLでは、文字列から数値への暗黙の型変換は規定されていないと考えられます。

製品の実装はどうか

 現実には多くのSQL製品で、文字列から数値への「暗黙の型変換」は実装されています。しかし、規格にない動作をどのような仕様で実装しているのでしょうか。私の調べた範囲では、以下の実装があります。

  • 文字列から数値への「暗黙の型変換」は許可せず、エラーにする(DB2 9.5まで)
  • 文字列から数値への「暗黙の型変換」の結果は浮動小数点数になる(MySQL)
  • 文字列から数値への「暗黙の型変換」は比較相手の型を見て「良きに計らう」(Oracle等)

 詳しくは「SQLの暗黙の型変換はワナがいっぱい | 徳丸浩の日記」を参照ください。
 このように、文字列から数値への「暗黙の型変換」は、多くの処理系で規格への拡張機能として提供されていますが、その仕様はバラバラです。規格に規定されていない機能を実装したことによる当然の結果と言えます。

「何が正しいのか」を考えるには基準が必要

 このように、文字列から数値への暗黙の型変換は規格による規定がないために、実装により仕様がまちまちです。極端な場合、同一製品でもバージョンにより挙動が変わる場合があります。DB2は前述のようにVer9.5までは文字列から数値への暗黙の型変換をエラーにしていましたが、Ver9.7にて、Oracleに近い仕様になりました。おそらくIBMは、SQLの本家としてのプライドを捨て、SQLの王者であるOracleの仕様にすりよったのでしょう。国際標準に規定されていないと言うことは、そのような大胆な仕様変更もあり得るということです。
 このように「暗黙の型変換」は規格化されていないことにより仕様が非常に不安定です。ある場合はエラーとなり、ある場合は浮動小数点数になるが故に不可解な動作をします。こういう不安定なものを選択することが、果たして「何が正しいのか」を考えた結果でしょうか。
 私はそうは考えません。正しい処理方法を考える上では、守るべき基準として、ISO/JIS/ANSI/ECMAの規格、RFCなどがあり、その上でプログラミング上の良い習慣などに従うべきだと考えます。
 このような根拠から、私は、「(数値も含めて)すべてのリテラルを文字列として処理」することは間違いだと考えます。たまたま特定の処理系(例えばPostgreSQL)でうまく動くからと言って、普遍的な真理であるかのように説明することはおかしい。『「何が正しいのか?」常に考えるようにしてください』という言葉は、そっくりそのまま大垣さんにお返しします。

11月24日(木)トレンドマイクロ&KCCS共催セミナーで講演します

トレンドマイクロとKCCSが共催するセミナーで基調講演することになりました。テーマはインターネットからのサーバー(主にWebサイト)を如何に防御するかについてですが、海外に関連会社を多数持つ企業のWebサイトのガバナンスの事例についても触れます。といっても、特殊な話ではなくて、どの企業・団体でも考えなければならない内容だと思います。
来週の開催ですが、まだお席があるようですので、興味のある方はお申し込みください。


日時:2011年11月24日(木) 14:30〜17:00(受付開始 14:00)
場所:トレンドマイクロセミナールーム 新宿マインズタワー12F
費用:無料


プログラム詳細、お申し込みはこちら https://trendmicro-partner.jp/partnerseminar/cyberattack/