SQLでエスケープなんてしたら負けかなと思ってる。

SQLへの安全な値の埋め込み方について、ここ数日で色々議論というか意見の投げ合いがありましたが、自分としての考えをまとめておきます。

1. SQLに値を埋め込む場合は、プリペアドステートメントプレースホルダを必ず使う

新規に開発する場合、99%以上はこれで十分なはずです。

値を埋め込まない静的SQLのみで、RDBMSAPIとして提供しているプレースホルダだけを使っている限り、絶対に安全です。もしそうでないとしたらそれはRDBMS脆弱性です。

ぱっと見プレースホルダっぽく見えて、ライブラリの中で値を埋め込んでRDBMSに投げている実装がたまにあるようなので、そういう場合はそこに脆弱性が存在する可能性が高まります。そのあたりは自分の使うライブラリやフレームワークの情報を収集してください。有名なところだと、PHPのPDOが提供する「動的プレースホルダ」がこのような実装になっていて、実際にShift_JISを利用した場合に脆弱性がありました。

なお、全てのRDBMSで使えるかどうかは分かりませんが、ストアドプロシージャを利用することで、テーブル名等についても展開が可能です。

ただし実装が不必要に複雑になったり、ストアドプロシージャが様々な理由で使えない場合もあるかと思います。そこで。

2. 文字種が判明している文字列のみ、テーブル名など限られた場所でSQL文字列に直接埋め込んで良いものとする

プレースホルダが利用できない(しづらい)状況として、日付や何らかの識別子をテーブル名(やパーティション名)に使わなくてはいけない場合があります。また、ソート順のORDERにカラム名を入れる場合などもあります。

その場合は、自分で生成した日付や、マスタデータとして持つ文字列など、文字種が完全に固定されたもののみを組み合わせるのであれば、その生成処理が正しく動いている限りリスクは。エスケープの必要の無い文字種のみを使うような設計をすれば良いです。

WordPressMovableType等でつかわれている「テーブル名プレフィックスという外部入力値」については、設計として「英数字とアンダースコアだけを使う」と決め、その通りに入力バリデーションを実施していれば、やはりエスケープの必要は無いはずです。

安全な設計をしましょう。

3. RDBMSの管理ツールなどの開発をしている場合は頑張ってエスケープする

RDBMSの管理ツールなどをあなたが実装しているのであれば、ユーザがうっかり作ってしまった記号まみれのテーブル名に対してもアクセスできないといけないかもしれません。

おめでとうございます!

ここまできて初めてエスケープが必要となります。

個人的には、RDBMSエスケープAPIや既存のライブラリを用いて「プレースホルダに値をエスケープして埋め込む」という関数をきっちり作り込み、そこに集約させるのが良いのでは無いかな、と思います。

決して、エスケープ処理自身を一から自分で書こうだなんて恐ろしいことを考えないでください。

その必要があるのは、あなたがRDBMS自身やそのライブラリの開発者の場合だけです。

4. レガシーコードのメンテをしている人も頑張ってなんとかする

SQL文字列に直接値をエスケープして埋め込んだコードが既にあり、修正する手間を掛けられない人。

正しいエスケープでもプレースホルダの利用に切り替えるでもリスクの許容でも好きな手法をお選びください。

エスケープする場合は上に書いたとおりです。

まとめ: 静的SQLプレースホルダを原則とし、エスケープが必要な場面そのものを可能な限り減らせ

アプリケーション開発者にとって、SQLインジェクション対策において必要な知識は、これに尽きると思います。

こちらの記事で指摘されるようなプリペアドステートメントのデメリットはありますが、「静的SQLプレースホルダ」の安全性と比較してもなお動的SQLが必要となる状況であれば、設計を見直すべきだと考えます*1

補足

SQLに限らず「エスケープは難しい」という認識は、全てのエンジニアが持っているべきです。

その認識が無いからこそ、プレースホルダが使える状況にもかかわらず動的にSQLを組み立ててしまうエンジニアが後を絶ちません。

補足2

大事なことを書き忘れましたが、SQL文法上の「エスケープ」の話です。

例えばLIKE句に食わせる文字列値の中の % や正規表現のメタ文字など、「文字列の中身」で必要なエスケープはもちろん必要ですし、そこから先こそこそがアプリケーションの責務だと考えます。もちろん「エスケープが必要な場面そのものを可能なかぎり減らせ」という主張は変わりません。

理想を言えば、LIKE句や正規表現などエスケープが必要な処理全てに対応する組み込み関数や、先頭一致を示すオペレータがRDBMS側で用意されていると、アプリケーション側でのエスケープが必要なくなり、より安全になるのではないかなと考えています。

*1:このあたりはさすがに宗教かなとは思います。