以下の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よりも複雑でテクニックも必要なエスケープを推奨しすぎると「ジョブセキュリティ」の疑惑をかけられかねないので、もう少し論理的になった方がいい。