昨日書いた安全なAPI過信症候群の処方箋 – execv/SQLite3編はSQLiteの仕様がRDBMSとしてエキゾチック過ぎて、さらっと書いたよくあるRDBMSでの「安全なAPI過信症候群」を全く理解して頂けなかったケースもあるようなのでもう一度書きます。
プリペアードクエリには色々問題もあり、セキュリティ対策としてそれだけ教えるのはNG、と考えている方はプリペアードクエリ過信症候群ではないので、このエントリを読む必要はありません。
安全なAPI過信症候群(同類にプリペアードクエリ過信症候群など):「安全」とされるAPIを使えば安全と、盲目的に信用し考慮すべきリスクを考えない症候群。ITエンジニアが発症し最も重要なセキュリティ対策である入力バリデーションを「必要ない、できない、セキュリティ対策ではない」エスケープは「必要ない、有害である」とする場合、かなり重度の場合が多い。
パラメータのエスケープ忘れによるSQLインジェクション対策としてプリペアードクエリが有効であるのは間違いありません。しかし、SQL文のセキュリティ対策として不完全かつ問題も多数あるので「プリペアードクエリ(プレイスホルダ)だけ知り、使えば良い」は誤りです。
セキュリティ専門家ならプリペアードクエリだけを知って使っていればよいなどと言いません。もしそんなセキュリティ専門家が居るなら、呼んでいただければ幾らでも議論をしに行きます。コメントでも構いません。公開で議論しましょう。(入力バリデーションはセキュリティ対策ではない、とする専門家が居るならその議論も歓迎します。私は入力バリデーションがセキュリティ対策ではない、などというセキュリティ専門家が居るとするなら、その人はセキュリティ専門家ではないと思います)
プリペアードクエリ過信症候群の処方箋
- SQL埋め込みを禁止できない
- 識別子を分離できない
- 一部の命令だけを分離できない
- 開発者が書くコードは「新しい」コードだけではない
- そもそもネイティブプリペアードクエリがない物もある(あった)
- プリペアードクエリだと極端に遅くなる場合がある
- プリペアードクエリが本物でない
- 開発者がプリペアードクエリをエミュレートしたい場合もある
- LIKEのエスケープ何?
- 開発者が知りたい、知るべきは仕様
これらは自分の環境では無関係かも知れませんが、現実の問題です。特定の環境のセキュリティ対策を考えるだけで良いなら「局所最適化」によるセキュリティ戦略もあり得えます。しかし、一般論に局所最適化を持ってきても問題を生むだけです。一般セキュリティ教育に実際の開発時に作るコーディング規約・仕様を持ってきて教えるだけでは有害であるということです。
参考:
SQL埋め込みを禁止できない
そもそもプリペアードクエリAPIは、プリペアード文を渡すパラメータへの埋め込みを禁止できません。つまり仕様から「Fool Safe」である安全なAPIとは言えません。利用者は「エスケープする代わりにプリペアード文のパラメータとして渡す」ことを理解して利用しないと間違えます。コード検査でよく見つかる間違いです。この間違いの原因は開発者が馬鹿だからではなく、教え方の問題だと考えています。
SQL埋め込みを禁止できないプリペアードクエリを「これを使えば安全」と教えるのは明らかに誤りです。
識別子を分離できない
テーブル名、コラム名これらが識別子です。プリペアードクエリは識別子を分離できません。パラメータの様に頻繁に変数となる物ではありません。しかし、現実に識別子をエスケープしなければならないことを理解していなかったためSQLインジェクションに脆弱になった例は数え切れません。
より複雑な処理を必要とする業務アプリケーションではストアードプロシージャが頻繁に利用されます。そして「処理をまとめる」ためにテーブル名やフィールド名が引数になっていることは珍しくありません。その結果、ストアードプロシージャがSQLインジェクションに脆弱になっていた例も多数あります。
ストアードプロシージャ内でもプリペアードクエリは使えますが、テーブル名、フィールド名など、識別子が引数の場合はプリペアードクエリは使えません。
一部の命令だけを分離できない
良し悪しは別にしてSQL命令(SQL 文の構文)がパラメータになっていることがあります。よくあるのはORDER BY句のDESC/ASCです。SQL命令がパラメータの場合もプリペアードクエリでは対応できません。こういう場合はホワイトリスト方式でバリデーションします。
開発者が書くコードは「新しい」コードだけではない
常に新しいコードだけを書いている開発者もいるかも知れません。しかし、現実には10年、20年前に書かれたコードのメンテナンスをしている開発者も多数います。
プリペアードクエリを使っていないコードは悪いコードだから全部書き直し、全てテストし直すべき!と主張しても意味がありません。プロジェクト化する決定権と予算をつける権限は普通のセキュリティ専門家や開発者にはありません。
そもそもネイティブプリペアードクエリがない物もある(あった)
一つ前の「開発者が書くコードは「新しい」コードだけではない」と関連しています。つい10年前にはプリペアードクエリAPIがないデータベースも珍しくありませんでした。
その時書かれたコードを使っていたり、事情によって現在も古い物を使っている方も現実にはまだ居ます。
プリペアードクエリだと極端に遅くなる場合がある
プリペア文を作った時にクエリ実行プランを作成するデータベースの場合、クエリ実行プランが実際のクエリパラメータに合わせて最適化されていないため、クエリの実行が極端に遅くなることがあります。
プリペアードクエリが本物でない
プリペアードクエリの様に見えるAPIでも、さまざまな理由でネイティブのプリペアードクエリではない場合があります。開発者がこのような場合にコードの安全性を確認するためにはエスケープ/バリデーションの知識が必須です。
開発者がプリペアードクエリをエミュレートしたい場合もある
チェックするのではなく、自分で実装する場合にエスケープ/バリデーションの知識が必須であることは言うまでもありません。
LIKEのエスケープ何?
LIKEのエスケープをしなくても攻撃可能な脆弱性を生む可能性は低いですが、エスケープの事を習っていない開発者は「意味のある文字が一文字でもあるとエスケープ/バリデーション処理が必要になる」というテキストインターフェース処理の基礎も習っていないことが普通です。その結果、LIKEのエスケープ何?という状況になってしまう開発者は多数います。開発者が悪いのではなく、教育が悪いことが原因です。
開発者が知りたい、知るべきは仕様
開発者が知りたいことは自分が使おうとしているシステムの仕様です。 システムは千差万別です。これ、個別の仕様を具体的に教える教えることはできませんが「一文字でも意味が有る文字がある場合、エスケープ/安全なAPI/バリデーションしないとセキュリティ問題が発生する(可能性が高い)」を知っていれば対応できます。
一般論としてのセキュリティ対策でも、個別具体例は有用です。SQLiteの動的カラム(マニュフェストタイプ)など、こんな仕様もあるのか!こんな問題もあるのか!という事実を知るのは役立ちます。
まとめ
結局のところ「プリペアードクエリだけ使えばOK」とするセキュリティ対策はプログラマでなく、指示通りにコードを書くコーダー向けの対策です。クリエイティブな開発者向けのセキュリティ対策ではありません。
未だに「プリペアードクエリだけ知って、使えば良い」と言う方はあまり居ないと思います。もしそうなら止めることをお勧めします。
私はプリペアードクエリを目の敵にしているのでないです。「安全なAPI過信症候群」は危険なので目の敵にしています。コード検査をしていても安全なAPI過信症候群の影響で正しくリスクを認識できず、セキュリティ対策としては最も対策が簡単な部類のSQLインジェクション脆弱性を作ってしまった例を何度も見ているからです。これは教育(書籍やネットの情報含む)の問題だと思います。
その一方で安全なAPIを広めるたり、作ったりすることにも大賛成です。マネジメントと同じでトップダウン/ボトムアップ、両方のアプローチがセキュリティ対策には必要です。
「安全なAPI」と言われている物でも「概ね安全」である物が多いです。つまり、昨日のブログに書いたexecvのようにヌル文字インジェクションに脆弱な物もあり、制限がある物が多いです。「完全に安全なAPI」ではない「概ね安全なAPI」の場合、その制限事項をよく周知させるようにして頂きたいです。
GoogleのセキュリティAPIの文書では制限事項がこれでもか!というくらい書いてある物もあります。そういった物が参考になると思います。