Java Tips | 実務ユーティリティ:SQL文字列安全化

Java Java
スポンサーリンク
スポンサーリンク

なぜ「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'

この変換を行うユーティリティを作ってみます。

タイトルとURLをコピーしました