「SQLインジェクション対策」でGoogle検索して上位15記事を検証した

このエントリでは、ネット上で「SQLインジェクション対策」でGoogle検索した結果の上位15エントリを検証した結果を報告します。
SQLインジェクション脆弱性の対策は、既に「安全なSQLの呼び出し方」にファイナルアンサー(後述)を示していますが、まだこの文書を知らない人が多いだろうことと、やや上級者向けの文書であることから、まだ十分に実践されてはいないと思います。
この状況で、セキュリティのことをよく知らない人がSQLインジェクション対策しようとした場合の行動を予測してみると、かなりの割合の人がGoogle等で検索して対処方法を調べると思われます。そこで、以下のURLでSQLインジェクション対策方法を検索した結果の上位のエントリを検証してみようと思い立ちました。

どこまで調べるかですが、以前NHKスペシャル“グーグル革命”の衝撃が放送された際に話題になった印象的な言葉「グーグル検索で…上位15位に入らなければこの世に存在しないのと同じです」にならい、少し多いですが「この世に存在する」上位15位までを調べてみました。
検索順位は変動するものなので、11月5日の朝に調べた結果のスクリーンショットevernoteの公開ページに保存しておきました。

それでは始めます。

1位:今夜分かるSQLインジェクション対策:Security&Trust ウォッチ(42) - @IT

また上野宣か」の名文句が生まれるきっかけになった有名な記事です。この記事への問題意識がきっかけとなって、安全なSQLの呼び出し方が生まれたという説もあります。このあたりの経緯については、上野宣氏が高木浩光氏にインタビューした「今夜こそわかる安全なSQLの呼び出し方 〜 高木浩光氏に聞いてみた:Security&Trust ウォッチ(60) - @IT」をご覧下さい。
さて、この記事ではWebアプリケーション側のSQLインジェクション対策として以下の記述があります。

  • SQLを埋め込むところで特殊文字を適切にエスケープ
  • シフトJISの場合には1バイト文字を整理
  • SQLの記述をなくすためにO/R(Object/Relational)マッピングを活用
  • 攻撃者に役立つ情報を与えないために、不要なエラーメッセージ(データベースが出力するエラーなど)の表示を抑止
  • バインドメカニズムの利用
http://www.atmarkit.co.jp/fsecurity/column/ueno/42.html

本当に箇条書きだけなんですね。これらの優先順位もなければ、andなのかorなのかも分からないので、高木浩光氏に「駄目な技術文書の見分け方」と書かれたのも仕方ない気もします。上野氏は友人なので叩かれ方が気の毒な気がしますが、これはライターとして気をつけなければ、という教訓にしたいと思います。

2位:SQL インジェクション攻撃とその対策 | Microsoft Docs

マイクロソフト社のtechnetの文書の1つですね。マイクロソフトの技術文書は非常に充実しているのですが、この文書はイマイチです。「攻撃とその対策」と題されている割には、対策については以下の記述があるだけです。

"SQL インジェクション攻撃" の対策方法には、お客様が開発・導入した Web アプリケーション、またはデータベース上のストアドプロシージャ等を改修し、意図しない SQL 文を受け入れないようにする必要があります。

http://technet.microsoft.com/ja-jp/library/dd362952.aspx

間違ってはないですが、「意図しない SQL 文を受け入れないようにする」ために何をすべきかは書いておらず、別のドキュメントにリンクされているだけです。
それでも、気を取り直して、リンク先の文書を読みましたがあまり良くありません。

Microsoft Security Advisory 954462 | Microsoft Docs

この文書の対策編は、ツールの紹介になっていますが、その筆頭に紹介されているHP Scrawlrが403エラーで参照できなくなっています(一種のリンク切れ)。
リンク切れはさておくとしても、SQLインジェクション対策としては、まずは正しいアプリケーションの書き方が先に来るべきと考えます。

第 2 回 SQL インジェクション その攻撃と対処

NECラーニングの講習会資料のようです。対策としては以下が書かれているのですが。

1.パラメータ化クエリまたはストアド プロシージャを使用
2.特殊文字エスケープ処理
ただし、すべての特殊文字エスケープすることは困難

「パラメータ化クエリ」というのはプレースホルダのことですが、日本ではあまり普及していないいい方ですね。また「ストアド プロシージャを使用」というのは誤りです。ストアドプロシージャの呼び出し方が間違っていると、SQLインジェクション脆弱性になる場合があるからです。ひょっとすると、「パラメータ化…ストアドプロシージャ」と言いたかった可能性はあります。Parameterized stored procedureという英語表記はあるからです。しかし、この読み方は「ウルトラC」という感じですので、たとえそうだとしても日本語としてよくありません。
また、特殊文字エスケープについて「すべての特殊文字エスケープすることは困難」と書いてあるのは意味不明ですね。これについては、11位のところで言及します。

SQL インジェクション | Microsoft Docs

この文書は、対策について量的に詳しく書かれていますが内容がよくありません。
書いてあることは基本的に入力値検証なのですが、入力値の検証ではSQLインジェクションの対策にはなりません。
また、「可能であれば、次の文字を含む入力は受け入れないでください。」とありますが、シングルクォート以外は対策として無意味です。詳しくは、このエントリを参照下さい。

; ' -- /* ... */ xp_

リンク先はまだあるのですが、もうよいでしょう。読者はリンク先を読んでいるうちに混乱して、訳が分からなくなりそうです。
この文書の問題点については、このエントリに先立ち日本マイクロソフトの高橋正和氏に連絡したところ、改善を検討して下さるとのことです。加えて、「それはそれとして、公開しているものは、公開している責任があるので、ズバッと行ってください!(そういうものだと思います)」というお言葉をいただきました。高橋さんのお人柄がにじみ出ていますね。ということで、改善についてはしばらく待ちたいと思います。

3位:SQLインジェクションの対策 | Think IT(シンクイット)

この記事は以前、「SQLのバインド機構は「エスケープ処理された値」をはめ込むのか - ockeghem(徳丸浩)の日記」や「続:SQLのバインド機構は「エスケープ処理された値」をはめ込むのか - ockeghem(徳丸浩)の日記」で批判しました。詳しくはそちらをお読みいただくとして、要は、元々「バインド値はエスケープ処理した後にプレースホルダにはめ込む」となっていた表現がおかしいと思って調べたら、安全なウェブサイトの作り方 改訂第3版の「バインド値はエスケープ処理されてプレースホルダにはめ込まれる」という表現を(ほぼ)丸写ししたものと判明したものです。その後、私の批判記事の一部を拾って「SQL文のひな型とバインド値は個別にデータベースに送られ、構文解析される」と修正されました。
「安全なウェブサイトの作り方」の方は、その後改訂第5版で以下のように改訂されています。少し長くなりますが、該当の項を引用します。

 SQLには通常、プレースホルダを用いてSQL文を組み立てる仕組みがあります。SQL文の雛形の中に変数の場所を示す記号(プレースホルダ)を置いて、後に、そこに実際の値を機械的な処理で割り当てるものです。ウェブアプリケーションで直接、文字列連結処理によってSQL文を組み立てる方法に比べて、プレースホルダでは、機械的な処理でSQL文が組み立てられるので、SQLインジェクション脆弱性を解消できます。
 プレースホルダに実際の値を割り当てる処理をバインドと呼びます。バインドの方式には、プレースホルダのままSQL文をコンパイルしておき、データベースエンジン側で値を割り当てる方式(静的プレースホルダ)と、アプリケーション側のデータベース接続ライブラリ内で値をエスケープ処理してプレースホルダにはめ込む方式(動的プレースホルダ)があります。静的プレースホルダは、SQLのISO/JIS規格では、準備された文(Prepared Statement)と呼ばれます。
 どちらを用いてもSQLインジェクション脆弱性を解消できますが、原理的にSQLインジェクション脆弱性の可能性がなくなるという点で、静的プレースホルダの方が優ります。詳しくは本書別冊の「安全なSQLの呼び出し方」のプレースホルダの項(3.2節)を参照してください。

ということで、妙なコピペのドキュメントではなくて、「安全なSQLの呼び出し方」を参照して下さい。

4位:http://www.computerworld.jp/topics/563/%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%83%BB%E3%83%9E%E3%83%8D%E3%82%B8%E3%83%A1%E3%83%B3%E3%83%88/115389/SQL%E3%82%A4%E3%83%B3%E3%82%B8%E3%82%A7%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E6%94%BB%E6%92%83%E3%81%AE%E3%80%8C%E6%9C%80%E6%96%B0%E5%82%BE%E5%90%91%E3%81%A8%E5%AF%BE%E7%AD%96%E3%80%8D

NRIセキュアテクノロジーの矢野さんの記事です。8ページにわたる長い記事ですが、対策は6ページに書かれています。このページの表1に対策が列記されています。しかし、この表1には、9項目も列挙されているので、どれが本質的な解決策で、どれが保険的な対策なのかが分かりにくいですね。答えを書くと、2番目の「バインド・メカニズムの利用」が本質的な解決策、それ以外はすべて保険的対策です。そうはっきり書いてくれればよいのに、はっきりとは書いていないのです。
また、対策が9項目もあるのに、それだけではまだ足りないようで、以下の記述もあります。

表1で挙げただけでなく、さらにWebシステム全般のセキュリティ・レベルを上げたいのであれば、WAF(Web Application Firewall)の導入も検討すべきであろう。

が、対策する側の立場に立つと、ゴージャスな対策に過ぎ、「とてもこんなにはできない」と思ってしまいそうです。著者もそう感じたのか、7ページにはコラムとして「しかし、現実の問題として、ここに記載している対策をすべて実施しようとすると、かなり大変である。」とも書いてあります。
全体としては面白いコラムですが、対策に関しては焦点が定まっていない印象を受けます。

5位:SQLインジェクション対策について

IPAのプレゼンテーション資料です。「安全なウェブサイトの作り方」を元にしているので大きな問題はありませんが、バインド機構のことを「エスケープの一種」と説明しているのが気になります。しかし、これは3位のところで説明したように、「安全なウェブサイトの作り方」が元々そういう説明になっていたことに由来しています。

6位:SQLインジェクション - Wikipedia

Wikipediaの解説です。間違ったことは書いていませんが、辞書的な記述で特に分かりやすいわけでもありません。これを参考にSQLインジェクション対策する人はあまりいないと思うのでこれくらいにとどめます。

7位:第42回 PostgreSQL 9.0に見るSQLインジェクション対策:なぜPHPアプリにセキュリティホールが多いのか?|gihyo.jp … 技術評論社

大垣さんの記事です。ブックマークコメントも参照ください。
この記事で、大垣さんは「SQLインジェクション対策の4原則」として以下を提示されています。

1. すべてのパラメータを文字列としてエスケープする
2. すべてのパラメータをプリペアードクエリのパラメータとして処理する
3. 文字エンコーディングの設定をAPIで行う
4. パラメータとして処理できない文字列はバリデーションを行う

原則1と原則2は重複して適用する必要はありません。どちらかを行います。

このうち、原則1(数値も文字列として扱う)には異論があります。詳しくは「数値項目に対するSQLインジェクション対策のまとめ」および「SQLの暗黙の型変換はワナがいっぱい」を参照下さい。
大垣さんは、「プリペアードクエリの功罪」という節の中で、「多くの開発者はプリペアードクエリさえ利用していれば大丈夫であるとしてエスケープ処理を軽んじてきました」と指摘しています。この文脈でのエスケープ処理とは、列名のエスケープを指しているようですが、それはともかく、確かに、プリペアードクエリ(プレースホルダ)さえ使えばSQLインジェクションは発生しないと言い切ってしまうと、誤解を招く可能性があります。
誤解されないためには、以下のように言わなければなりません。

  • SQL文を動的に(文字列連結などで)組み立てない

SQL文を動的に組み立てないとすると、パラメータを渡す際にはプレースホルダを使うしかありません。そして、SQL文を動的に組み立てなければ、SQLインジェクション脆弱性の余地はありません。
 単にプレースホルダを使うというだけでは、例えば以下のようなケースが問題になります。

$sql = "select * from $table where id=?";

この例は、テーブル名を外部から指定できるという想定ですが、$table = 'mytable; drop table mytable --'; などとすることで、任意のSQL文を追加できる場合があります。MS SQL ServerPostgreSQLの場合に、攻撃が成功する可能性が高くなります。
SQL文を動的に組み立てようとする場合、前提条件により対処が変わります。

Webアプリケーションの初心者が書いてしまった場合

Webアプリケーションの初心者の場合、そもそもテーブル名を外部から指定できる仕様を見直すべきでしょう。例えば、組み立て後のSQL文が何種類かに限定できる場合、固定のSQL文を切り替えて使うなどの対処が考えられます。

アプリケーション・フレームワーク等を作成している場合

この場合、テーブル名を指定したい動機はあり得ますが、その場合でも任意テーブル名を指定できる必要はないはずで、安全にテーブル名を指定する仕組みを考える必要があります。

phpMyAdminのようなデータベース管理ツールを作成している場合

phpMyAdminのようなツールを開発している場合、元々任意のSQL文を利用者が実行できるわけで、その場合はSQLインジェクションによる「攻撃」のシナリオを考慮する必要はありません。しかし、ツールが生成したSQL文にエラーがないことは必須条件なので、テーブル名等に記号などを許容している場合は、SQLの文法に従ってテーブル名をエスケープする必要があります。

ということで、アプリケーションプログラマの従うべき原則としては、「SQL文を動的に(文字列連結などで)組み立てない」に従うことをお勧めします。O/Rマッパーやアプリケーションフレームワークを開発するような人は上級者だと思うので、自己責任でSQL文を安全に組み立てる方法を考えてくださいw

8位:漢(オトコ)のコンピュータ道: SQLインジェクションとは何か?その正体とクラッキング対策。

奥野さんのブログ記事です。MySQLのエキスパートですね。対策としては以下のように書かれています。

SQLインジェクションの最も確実で根本的な対策は、プリペアードステートメントを利用することである。問題は、SQLを組み立てる過程にあるので、動的にSQLを組み立てなければ原理的にはSQLインジェクションは発生しない。Webのフォームから受け取った値は、パラメータとしてプリペアードステートメントに渡せばいいわけである。ただし、プリペアードステートメント自身を動的に組み立ててしまうと元の木阿弥なので注意しよう!

完璧ですね。付け加えることはありません。

9位:IPAがまとめたSQLインジェクション対策マニュアル

「安全なSQLの呼び出し方」を紹介してくださっているブログ記事です。ありがとうございます。付け加えることはありません。

10位:SQLエスケープにおける「\」の取り扱い

私のブログ記事です。「SQLのエスケープ再考」の続編なのですが、SEO上は続編の方が上位に出ています。
どうも、これらの記事が高木浩光さんの目にとまって2008年のWASForumでの講演依頼をいただいたようです。高木浩光さんの日記には以下のように書かれています。

SQLインジェクション対策については、HASHコンサルティングの徳丸さんにお願いしました。今やSQLインジェクションの対策方法は広く理解されているはずのようにも思えますが、どうもそうでもないようです。いまだに「サニタイズ」的な対策を挙げる検査業者、コンサル業者がいるようで、しばしば、そういった業者がはてなブックマークで血祭りにあげられている事例も散見されるところです。blogで「SQLエスケープ再考」といったエントリを書かれている徳丸さんに、このあたりのことについて結論を出していただきます

http://takagi-hiromitsu.jp/diary/20080620.html

これを読んだ私のプレッシャーを察していただけるでしょうか。
但し、このWASForumの場では結論までは出ず、その後私自身がIPAに召還されて、高木さんやIPAのスタッフとともに「安全なSQLの呼び出し方」を執筆して、ファイナルアンサーに至りました。

11位:被害が続くSQLインジェクション攻撃,もう一度対策を見直そう | 日経 xTECH(クロステック)

ITproに日本IBMの菅野さんが書かれた寄稿記事です。2008年春というと、MS SQL Serverを狙ったSQLインジェクション攻撃が頻繁に発生していた時期にあたります。記事の前半は攻撃手法に関する説明で興味深く価値の高い内容だと思いますが、問題は後半の対策手法です。
「最も重要なのはWebアプリケーションでの対策」という見出しはとても正しいのですが、その次の見出し「不審な入力値は無効な文字列に変換」を見るに、身構えてしまいますね。そして、予想通り、いや、予想をはるかに上回る内容が続きます。

今回の攻撃では,WebアプリケーションにPOSTされる文字列の中にDECLARE文やEXEC関数が含まれていた。このような本来入力値として渡される可能性がない入力値や文字列を厳格にチェックし,無効化すれば脅威は回避できる。

http://itpro.nikkeibp.co.jp/article/COLUMN/20080514/301660/?P=2

DECLARE文やEXEC関数が「本来入力値として渡される可能性がない入力値」なんて軽々しく言って欲しくないですね。それに、DECLAREやEXECなどのキーワードによるブラックリスト検査は、攻撃者により回避されやすいという問題があります。この種の検査はWAFに任せておけばよく、アプリケーション側では本質的な対策に専念すればよいでしょう。
次には以下の表があります。

【入力の例】
DECLARE%20@S%20NVARCHAR(4000);SET%20@S=hogehoge EXEC(@S);

  ↓          ・特殊文字を文字として扱うために「\」を挿入  / 「;」は削除

【入力値チェックの結果】
DECLARE\%20@S\%20NVARCHAR\(4000\)SET\%20@S\=hogehoge EXEC\(@S\)

ここでの指摘は以下の通りです。

  • 「入力の例」として提示されている文字列は、エスケープの必要な文字は含まれていない。すなわち、何もする必要はない
  • 入力の例に「%20」が含まれているのでパーセントエンコードをデコード前と思われる。入力値チェックはパーセントデコード後に行うべき
  • MySQLの場合は、ここで追加した「\」は単に無視されるが、多くのDBでは通常の文字として扱われる。すなわち、勝手に「\」を挿入するとバグになる
  • 勝手に「;」を削除するな。SQLインジェクション対策としては無意味(参照:SQL Serverが狙われるにはまだまだ理由がある

ということで、この記事の著者はSQLもWebアプリケーションもまったく分かっていないようです。このような記事を参考にしてはいけません。

12位:http://lets.postgresql.jp/documents/tutorial/with_php/against_sql_injection/escape_quote

第四企画の坂井さんがLet's Postgresの記事として寄稿されたものです。
この記事は素晴らしい。PostgreSQLエスケープ手法によりSQLインジェクション対策する際の技術的な注意点が正確きわまりなく書かれています。細部に至るまで、この内容に同意します。PostgreSQLを深く知りたい方すべてにお勧めです。また、MySQL界隈の方はぜひ、MySQL向けのこのように優れた文書がないことに切歯扼腕して、MySQL向けの優れた解説を書いてください。私が知らないだけであれば教えてください。
しかしながら、この記事の冒頭に「プレースホルダを用いてより効果的な対策を行うケースに関してはプレースホルダ編を参照してください」とあるように、対策の本命はプレースホルダを使ったものです。というわけで、実用的にはこの記事の姉妹編である「PHPでのSQLインジェクション対策 - プレースホルダ編 | Let's Postgres」を参照してください。こちらも素晴らしい内容です。

13位:MySQLとPHPにおけるSQLインジェクション対策について

表題通りの内容ですが、記事中に「今回は基本的な方法のご紹介でしたが、これらを施すだけでもSQLインジェクションに関する脆弱性はかなり軽減することと思われます」と控えめなコメントがあるのが、必ずしも謙遜ではないと思われます。不正確な内容なので、読まない方がよいでしょう。

14位:無料で使えるSQLインジェクション対策スキャナ トップ15*ホームページを作る人のネタ帳

SQLインジェクション対策スキャナの海外の紹介記事を翻訳(トップ15のうち上位4つのみ)した記事ですが、2007年という少し古い記事であること、元記事は既に削除されて読めないこと、紹介者自身も試していないと思われることなどから、読まなくて良い記事だと思います。

15位:SQLインジェクションを考える

Webサイト側ではなく、サイト利用者の側でのSQLインジェクション対策について検討した記事です。
面白い着眼点ですが、結論としては、マルウェア感染の脅威については最新のパッチ適用とウイルス対策ソフトの導入、個人情報漏洩については決定打はないということで、特別目新しい内容はありません。

結局どうすればよいか

冒頭に記した通り、SQLインジェクション対策には既にファイナルアンサーがあります。それは「安全なSQLの呼び出し方」に書いた内容ですが、まとめると以下の通りです。

この2つを守っている限り、アプリケーションやSQL呼び出しライブラリなどにバグがあっても、原理的にSQLインジェクション脆弱性は発生しません。
もしもSQLを動的に組み立てている場合、アプリケーションにバグがあると、SQLインジェクション脆弱性になります。
静的プレースホルダではなく動的プレースホルダを使っている場合、SQL呼び出しライブラリにバグがあると、SQLインジェクション脆弱性が生じる場合があります。具体手例としては、JavaとMySQLの組み合わせでUnicodeのU+00A5を用いたSQLインジェクションの可能性 | 徳丸浩の日記ぼくがPDOを採用しなかったわけ(Shift_JISによるSQLインジェクション) | 徳丸浩の日記を参考にして下さい。この2例は既にライブラリ側で改修済みです。安全なSQLの呼び出し方でも説明しています。

まとめ

SQLインジェクション対策」でGoogle検索した上位15件の内容を検証しました。
ここまで説明したように、上位15件の中には、SQLインジェクションの対策手法を説明した上質の文書はあまりありません。12位の「http://lets.postgresql.jp/documents/tutorial/with_php/against_sql_injection/escape_quote」はとても良い解説ですが、初心者には少し難しいと思います。
初心向けの解説としては、VOYAGE GROUP須藤さんの素晴らしいブログ記事「Webアプリケーションとかの入門本みたいのを書く人への心からのお願い。」がおすすめです。現役バリバリのWebエンジニアがこれを書いたところに圧倒的な説得力があります。この記事は、次のように始まります。

SQLインジェクションについて書くときに以下のメッセージを必ず含めて欲しいです。

http://d.hatena.ne.jp/ajiyoshi/20100409/1270809525

素晴らしい。以上の引用をもって、本エントリ全体のまとめに代えたいと思います。


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