以下のTogetterでまとめて頂いているが、大垣靖男氏と「何故氏はSQLインジェクション対策においてプリペアドステートメントの利用よりも入力データのエスケープ処理を優先するのか」について議論させて頂いた。
SQLインジェクション対策としてのプリペアドステートメントとエスケープについての議論
僕の疑問は一番最初の方にもあるが、「プリペアドステートメント+プレースホルダーで複雑な自作エスケープなどせずともシンプルにさほどの技術力も無く少なくとも『SQLインジェクション対策』としては機能するのに、何故そこまでエスケープ処理対応を一義に拘るのか」という点だった。
この優先順位度合いは以下のブログの部分からも明らかだ。
実際の開発に利用するコーディングルールでは出力先によって「API > エスケープ > バリデーション」の順番になる場合もありますが、セキュリティを維持するための重要度は「エスケープ > API > バリデーション」であることは変わりません。
さて経緯は上記Togetterに譲るが、結論として何故氏はそのような無謀な主張を延々繰り返すか、氷解したような気がする。
少しツイートもしたが誤解があるかも知れないのでもう少し詳しく説明しておきたい。またこれは以外に面白い視点も内包しているかも知れない。
ツイートの繰り返しにはなるが、通常プリペアドステートメントのためには文字列変数にSQL文を格納してこれにプレースホルダを使用してバインドを行うだろう。
ここで変数に格納されるSQL文は通常リテラル的な扱いであり、そもそもプログラミング時点で確定しており、多くの場合は実行時に条件やソートを変えることはしない。
こうした仕様を一般には「静的SQL」と理解している。
もしこのSQLが何らかの外部パラメータなどで動的に変化するなら一種の「動的SQL」と呼べるかも知れない。しかしバインド変数がプレースホルダを通じて渡されるのであればそれは静的SQLとは「SQLインジェクションへの脅威」という点では何ら変わらないだろう。
ところで大垣氏は次のような回答をしている。
@rocaz なぜプリペアードクエリを使っていてSQLインジェクションに脆弱なコードを書いてしまうのか?を考えると分かります。多くはパラメータ埋め込みに対するリスクを知らないからです。知らないなら教えるが対策。でどう教えるか?エスケープ無しだとインジェクションできるでしょ?と。
— Yasuo Ohgaki (大垣靖男) (@yohgaki) 2013, 12月 9
@rocaz なぜ正確なテキスト処理が必要なのかを知らない・理解していないから、プリペアードクエリを使っていてもインジェクションに脆弱なコードを書いてしまいます。その原因をどう無くすか?を考えています。「APIを正しく使えばインジェクションできないから使え」は原因が残ったままです
— Yasuo Ohgaki (大垣靖男) (@yohgaki) 2013, 12月 9
プリペアドステートメントが前提のインジェクションと呼んでいる。
これは察するに「プリペアドステートメントのためのSQL文変数を用意しながら、プレースホルダを利用せず(あるいは両用しながら)、外部からのパラメータをSQLに埋め込む」プログラミングの意味では無いか。
しかし一般にそれは「プリペアドステートメントを用いたSQLインジェクション対策」とは呼ばない。また「静的SQL」でもなく、それは明らかに「動的SQL」であろう。
察するに、氏が「サイトの検査をしていれば分かるようになる」と発言していることから、そうした例もあることを見てきたと言うことかも知れない。
そして想像するに、そうした状況、つまり「開発者が適切に静的SQLを利用せずに勝手に外部からのパラメータをSQLに埋め込める」=「プリペアドステートメントという手法は完璧では無い」と思い込んだとしたら、「仕方が無いので」一番最初にエスケープせざるを得ないという結論に縛られてしまっているのでは無いかと思うのだ。
と仮説すると、以下の一見意味不明な発言もよく理解できるようになるのである。
@rocaz プリペアードクエリには欠陥があるのでセキュリティ対策”教育”の第一番にはなりません。識別子、SQL語句の分離ができない。仕組みとして変数埋め込みを禁止できない。この欠陥はどうしようもないです。
— Yasuo Ohgaki (大垣靖男) (@yohgaki) 2013, 12月 9
@rocaz まず、SQL文への変数埋め込みをどうやって防ぐのですか?
— Yasuo Ohgaki (大垣靖男) (@yohgaki) 2013, 12月 9
やはり一般的には、であるなら「安全な静的SQLの発行方法」を開発者に啓蒙すればいいだけだ。つまり
- プリペアドクエリでSQL発行は行うこと
- この場合変数のバインドは必ずプレースホルダを用いること。SQL文の文字列操作は禁止
であり、これほどシンプルなことも無いだろう。
しかし氏の考えは違うのだ。欠陥という以上また禁止できないという以上、「PrepareのためのSQL文が変数でかつ開発者が記載する以上、勝手に外部からのパラメータをSQLに埋め込んでしまうかも知れない」=「完璧では無い」=「エスケープが必要」となるのである。
だがこれは既にまがう事なき「動的SQL」としての考え方である。少なくともプリペアドクエリを活用した静的SQLと見なすエンジニアは他にはいまい。
何故氏がそこまで「考えすぎた」のか、「開発者への静的SQLの啓蒙」という考え方をしなかったのかは未だに不明だ。
だが「正しい静的SQLの発行方法」を理解していれば、これまでのような不可思議なほどの「エスケープ対策押し」はあり得なかったのでは無いか。
結論。
大垣氏は簡単な静的SQLよりも複雑でテクニックも必要なエスケープを推奨しすぎると「ジョブセキュリティ」の疑惑をかけられかねないので、もう少し論理的になった方がいい。
流石に読解、曲解が酷い気がする、、、
大垣氏って誰?大垣なら知ってる
以下の理由で大垣氏はやらなきゃ教に入信したのではないでしょうか?
(たしか氏はPHPの権威でしたよね?)
https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/502.html
ただし、PEAR(PHP Extension and Application Repository)を使ってDBアクセスを行い、PEARでのプリペアドステートメントを利用している場合は、注意が必要である。
PEARは、コミュニティーにより運営されるプロジェクトのことで、コード配布およびパッケージ管理のためのシステムやPHPのコード作成に関する標準スタイル、PHP拡張モジュール・コミュニティライブラリ等を提供することを目的にしている。
ここでは、PEARをPHPの拡張モジュールとして記述している。
このPEARのプリペアドステートメントは内部的にバインド変数をエスケープしてSQL文を組み立るため、有効に機能していない。
大垣氏の主張、このあたりのgihyo.jpの記事を見る限り、ちゃんとプリペアドSQLを理解しているように見えます。
http://gihyo.jp/dev/serial/01/php-security/0042
http://gihyo.jp/dev/serial/01/php-security/0014?page=2
おそらく、テーブル名やorder by の値をバインドできないから、その部分に変数を使ってしまってSQLを組み立ててしまい脆弱になるケースが多いというのが氏の主張なのでは? と思います。(それをなぜtwitterで分かりやすく説明できなかったかは不明ですが)
僕も恐らくご指摘のところ + プレースホルダを「必ずしも使うことを開発者に強制できない」という理由から彼はプリペアドステートメントでは(完全に)対応できないと結論づけてエスケープによる対応を主張しているのだと思います。
しかしながら既に僕も指摘している通り、それなら「プリペアドステートメント + プレースホルダを確実に利用すること」を啓蒙する方が確実に安全になるわけで、何故彼が頑なにエスケープに拘るかは相変わらず不明です。
因みにTwitterでも指摘していますがSQLステートメントが多パターンに及ぶ場合というのはあり得る訳ですがそれはそれだけのパターンのSQL文定数を用意することは多くの場合可能ですし、少なくとも外部からの入力をそのまま適用しなければいいだけなので、決して積極的にプリペアドステートメントが脆弱である理由にはなり得ないと思います。
「静的SQL」と僕が呼んだのはこの「プリペアドステートメント + プレースホルダを確実に利用すること」を明確にしたかったためなのですが、彼の前提は「プリペアドステートメントを利用する時点で動的なSQL組み立てである」と理解するようで、それをして僕は「彼はプリペアドステートメントを(的確に)理解していない」と表現しました。
また「どうせテキスト処理はプログラミング学習者にとっては必須なので同じ事なのだからここでもエスケープを用いれば良い」「教育のためにはエスケープを使用することは必然」などの発言は手段と目的を完全にはき違えたものであり、もっと言うと結局のところは彼はエスケープによる対応を主張しすぎて振り上げた手を振り高いプライド故に下ろせなくなった愚か者であり、故に誰にも理解できない理屈を振り回すのであり、研究者には値しないと考えるものです。