ツイッターでの議論を見て「SQLエスケープを教える必要はない」とする原因は「教育の基本」と「セキュリティの基本」の理解不足が「根本的な原因」だと解ってきました。この事についてもブログを書くかも知れませんが、今日は「オレオレSQLセキュリティ教育」、言い換えると「自分の環境に特化したSQLセキュリティ教育」について書きます。一般論・基礎としてのセキュリティ教育は、自分の環境に特化したセキュリティ教育では困る、という話です。
このエントリは「貴方が普段言っている事が間違っている」と非難または攻撃しているのではありません。実務で「プリペアードクエリ・プレイスホルダ・ORMを使いましょう」と言うことは全く間違っていません。セキュリティ教育はこういう手順で教えたほうが解りやすいです、と提案しています。
ツイッターでSQLセキュリティ教育に「エスケープは必要ない」とする方たちの代表的な意見は次の通りです。
- 識別子のエスケープなんて普通使わない
- プリペアードクエリ/プレイスホルダで十分
- エスケープは間違えやすいから教える必要はない
これらが代表的な意見だったと思います。これらは「自分の環境なら必要ない」とする考え方です。順番に考えてみます。
- 識別子のエスケープなんて普通使わない
「自分は使っていない、レアケースだから教える必要はない」とする意見です。自分は使わないからセキュリティ上必要でもエスケープは教える必要がないとする、自分の環境に特化したセキュリティ教育です。
DBスキーマを操作するプログラムを書く場合、識別子エスケープは必須です。DBスキーマを操作できるプログラムはそもそも管理者だから、インジェクションできても問題ないと意見もありました。「SQLインジェクションはしたい放題」というコードを納品して良いでしょうか?さらにCSRFに脆弱で、丸ごとDBを消されてしまっても問題ないでしょうか?識別子エスケープは必要です。
検索などを効率化する為、古いデータはアーカイブテーブルに移動します。複数のアーカイブテーブルがある場合、基本となるSQL文は1つでテーブル名を変数で指定します。プリペアードクエリは識別子を分離できないので、確実に安全なSQL文には識別子エスケープが必要です。
アプリによっては識別子エスケープが必要ないかも知れません。しかし、現実の世界では必要となる場合があります。「自分は使わないから必要ない」は一般論として通用する論理になっていません。
- プリペアードクエリ/プレイスホルダで十分
「自分はプリペアードクエリ/プレイスホルダで十分」だとする意見です。自分が作っているアプリやライブラリやフレームワークでは、プリペアードクエリ/プレイスホルダだけで十分かも知れません。これも自分のコード以外を考慮しない、自分の環境に特化したセキュリティ対策です。
例えば、DB抽象化レイヤーでquoteメソッドをサポートする場合、エスケープ処理が必ず必要になります。DBによってはエスケープAPIが無い物もあります。この場合、自作する必要があります。コード検査でエスケープ漏れが見つかり修正する場合、動的SQLを静的SQLに書き換える事も可能です。しかし、普通は即エスケープ処理を追加して脆弱性を修正します。識別子にエスケープが必要だった場合、脆弱性の修正にはエスケープするしかありません。
理由も書きましたが「識別子エスケープなんて要らない」とする議論が論理的に破綻しています。この議論も論理的に成り立ちません。手元のコードはプリペアードクエリ/プレイスホルダで十分かも知れませんが、現実の世界で「十分」とする論理は成り立ちません。
- エスケープは間違えやすいから教える必要はない
「自分はエスケープ処理は間違えやすいと考えるから必要ない」とする考えです。これも、周りの開発者のスキル・知識(つまり受けた教育)に依存した問題意識です。つまり自分の環境に特化したセキュリティ教育です。
エスケープはプリペアードクエリより間違えやすい。これは正しいでしょう。変数が数値だからエスケープは必要ないと勘違いして脆弱になったりします。しかし、この間違いの原因は「正確なテキスト処理」つまりエスケープを理解・実践していなかった事が原因です。エスケープが無いと危険と教育されていない事が原因です。(注:数値リテラルのみサポートする一部DBではバリデーションが必要)
プリペアードクエリはエスケープより間違えやすい、も正しいです。プリペアードクエリさえ使えばOKと教えられた開発者が、識別子やSQL語句を埋め込んでしまうケースは少なくありません。しかし、エスケープ処理を正しく理解している開発者ならこのような間違いはしません。
理由も書きましたが前の2つの意見が論理的に破綻してます。したがって「エスケープは間違えやすいから教えない」とするセキュリティ教育も論理的に破綻しています。
完全なSQLインジェクション対策とは以下のような対策を言います。
まとめ
自分の環境に特化したSQLセキュリティ教育は方法論として論理的に破綻しています。筋の通らないセキュリティ教育では「漏れ」が発生します。「漏れ」=「脆弱性」です。「脆弱性」を無くすためのセキュリティ教育が脆弱である事は許されません。
最後に自分の環境に特化したセキュリティ教育と言っている教育であってもフレームワークやライブラリの安全な使い方などの教育なら許されます。一般論ではなく「特定の環境をどう安全に使うのか」という教育なら、エスケープ処理など出力セキュリティの基礎知識はあるという前提で省略し環境特有のAPIや仕様などを解説して当然です。
しかし、「基礎としてのWebアプリセキュリティ教育でSQLのエスケープは教えなくて良い」は論理的にも、セキュリティ対策的にも間違っています。
より良い教育方法があったら是非採用したいです。エスケープファースト、この順序は変えられないで書いているように、出力セキュリティ教育の一般論・基礎知識としてより良い方法があったら是非教えて下さい!
追記
はてなブックマークで「 pullphone “エスケープは必要ない” そんなこと誰も言ってないと思うし、誰も思ってないだろ。そんなこと言ってる奴いたらそっちのほうが叩かれてるだろ」とコメントがありました。こういった状況になればJavaScriptインジェクション対策も進むと思います。
こういう穴だらけ/出鱈目なSQLインジェクション対策を平気で公開し続けている方も居るので、オレオレSQLインジェクション対策は破綻していても、無くなってはいません。
同じくはてなブックマークで「koyancya そんな ORM があるのか -> “変数を一切埋め込む事ができないORMなど教えるべきでしょう」とコメントがありました。この記述はシンプルにエスケープの重要性を訴えた方が良いとアドバイス頂き削除してしまいましたが、私もそんなORMを知りません。ORMを使えばOK、ではプリペアードクエリ/プレイスホルダを使えばOK、と同じ間違いをします。
同じく「Kenji_s SQL SQLインジェクションとは無関係ですが、LIKE句のワイルドカードのエスケープ処理は多くの場合必要なので、エスケープを教えないのは無理がある」さすが鈴木さん。コード検査でもこの間違いを見かけます。