なぜ「SQL文字列安全化ユーティリティ」が業務で重要なのか
業務システムでデータベースを扱うとき、ユーザー入力を検索条件や更新値として SQL に渡す場面は必ず出てきます。 このときに、ユーザー入力をそのまま SQL 文字列に連結してしまうと、SQLインジェクション という代表的な攻撃の入り口になります。
SQLインジェクションは、攻撃者が「意図的に壊れた文字列」を入力し、それをそのまま SQL に埋め込ませることで、 本来想定していない SQL を実行させてしまう攻撃です。 例えば、ログイン画面で次のような文字列をユーザー名として入力された場合を考えます。
' OR 1=1 --
これをそのまま SQL に連結すると、次のような危険なクエリが出来上がります。
SELECT * FROM users WHERE name = '' OR 1=1 --' AND password = 'xxx';
OR 1=1 によって条件が常に真になり、-- 以降がコメント扱いになることで、 パスワードチェックが無効化されてしまいます。 こうした攻撃を防ぐために、「SQL文字列安全化」というテーマが出てきます。
ただし、ここで最初に強調したいのは、本質的な対策は「文字列安全化」ではなく「プレースホルダ(PreparedStatement)を使うこと」 だという点です。 そのうえで、「どうしても文字列を扱わざるを得ない場面」での安全化ユーティリティを考えていきます。
ステップ1 本当にやるべきことは「SQLを文字列連結しない」こと
PreparedStatement を使うのが王道です
まず、プログラミング初心者の方に最初に伝えたいのは、 「SQLを文字列連結で組み立てるのをやめる」ことが最大の防御になる という事実です。
悪い例から見てみます。
// 絶対にやってはいけないパターン
String sql = "SELECT * FROM users WHERE name = '" + userInput + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
Javaこのように、ユーザー入力 userInput をそのまま ' で囲んで連結すると、 攻撃者は簡単に SQL の構文を壊して別の意味に変えることができます。
正しいパターンは次のようになります。
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, userInput);
ResultSet rs = ps.executeQuery();
Javaここでは、SQL の中に ? というプレースホルダを置き、 setString で値をバインドしています。 このとき、userInput は「SQLの構文」ではなく「値」として扱われるため、 どんな文字列が入っていても、SQLインジェクションにはなりません。
重要なポイントは、「SQL文字列安全化ユーティリティ」は補助的なものであり、 基本は必ず PreparedStatement を使う、という設計にすることです。
ステップ2 それでも文字列を扱わざるを得ない場面を整理する
LIKE 検索や動的な ORDER BY などの「グレーゾーン」
PreparedStatement を使えばほとんどの場面は安全に書けますが、 それでも文字列を扱わざるを得ない場面がいくつかあります。
代表的な例として、次のようなものがあります。
LIKE検索で%や_を含むパターンを扱うときORDER BYのカラム名やソート方向を動的に切り替えたいとき- SQL テンプレートをログ出力や設定ファイルとして扱うとき
これらの場面では、「値は PreparedStatement でバインドしつつ、 一部の構文要素を文字列として扱う」という設計になります。
ここで「SQL文字列安全化ユーティリティ」が役に立ちます。 例えば、LIKE 検索用にユーザー入力を安全なパターンに変換する、 ORDER BY 用に許可されたカラム名だけを選ぶ、といった処理です。
ステップ3 文字列リテラルとして埋め込む場合の安全化を考える
シングルクォートの扱いが最重要です
どうしても文字列リテラルとして SQL に埋め込む必要がある場面では、 最も重要なのは シングルクォート ' の扱い です。
SQL の文字列リテラルは、次のような形をしています。
'ここに文字列が入る'
この中に ' が含まれていると、文字列が途中で閉じてしまい、 続きが別の構文として解釈される危険があります。
そこで、SQL の世界では ' を ''(シングルクォート2つ)に置き換えることで、 文字列の中に ' を含めることができます。
例えば、次のような文字列を考えます。
O'Reilly
これを安全な SQL 文字列リテラルにすると、次のようになります。
'O''Reilly'
この変換を行うユーティリティを作ってみます。
