セキュアコーディングを実践する際にSQLインジェクション対策としてプレースホルダを利用するという事は既に常識の範疇だと思われる。
フレームワークでSQLを自動生成していれば意識していないかもしれないが、その裏側ではプレースホルダがしっかりと利用されているはずだ。
プレースホルダの種類
プレースホルダはSQLインジェクション対策のためだけの仕組みではなく、データベースやコネクタの種類によっては動的プレースホルダと静的プレースホルダと言う2種類の違いが出てくる。その点を少し掘り下げてみた。
プレースホルダとは?
SQL文中に動的なパラメータを組み込む機構(プリペアドステートメント)で利用する動的パラメータ挿入位置のポインタであり、従来は以下のように疑問符(?)で指定する。
SELECT id, name FROM users WHERE id = ?
ただし、上記だけでは複数の動的パラメータがある場合に可読性が悪く不具合の発生原因にもなりやすい。
そのため、以下のような名前付きプレースホルダ(「:」や「@」)が各データベースに拡張実装されている。
SELECT id, name FROM users WHERE id = :id
プリペアドステートメントとは?
本投稿では詳細は割愛するが、データベース側で動的なSQLを解析する際に解析結果のキャッシュが利用されるようになる(解析負荷軽減→別途問題もあるので後述を参照)
先ほどのSQLを例にすると、idが1のデータを取得するSQLも100のデータを取得するSQLも文字列上は同じSQLとなるからだ。
※別途問題点
- キャッシュを利用する=その分リソースを消費するため過去メモリーリークの不具合があった
- データ取得のスループットが悪くなる
- 実際の値ではなくSQL分の構造のみで実行計画を検討するためアクセスパスが最適にならない可能性がある
- SQLを直接実行した結果とアプリケーションから実行した場合に極端に実行速度に差異が出る場合の理由の一つはこれ
静的プレースホルダと動的プレースホルダとは?
静的プレースホルダはデータベース側でSQLを解析し値をバインドするため、上記にて説明してきたプリペアドステートメントのポインタとして利用される。
一方、動的プレースホルダではプレースホルダが入ったSQLではなく、プレースホルダ位置にエスケープされた値がセットされたSQLとなる。(そのまま実行できる値がセットされたSQLとなっている)
SELECT id, name FROM users WHERE name = 'Let\'s go'
※ '
は ''
のように2個重ねるエスケープもあり、こちらの方が一般的な気もする。
動的プレースホルダの主なメリットデメリット
メリット
実際の値がSQL文にセットされてSQLが実行されるため最適なアクセスパスを取りやすくスループットが向上する。
デメリット
ソフトウェア側でエンコードするため脆弱性が入り込む余地がある。(過去、MySQL Connector/J でプレースホルダを利用していてもSQLインジェクションが発生する不具合があった。)
参考:MySQL Connector/J における SQL インジェクションの脆弱性
MySQLをデータベースに選択する場合には、上記のメリットとデメリットからどちらを選択するのかを決める必要がある。
ちなみに過去のバージョンではデフォルト値は静的プレースホルダだったが、特定のバージョンからは動的プレースホルダがデフォルトとなった。
最後に
プレースホルダはセキュリティ対策として言うまでもなく利用するべきですが、ただ利用するだけでなくその意味を理解する事でより柔軟な選択をする事が出来る。
プレースホルダの件に関わらず、常識だと思っている事についてもその意味を理解する事で違った視点で物事が考えられるようになるのではないか。
参考:安全なSQLの呼び出し方