オープンソースで作る Java+DB

特集 オープンソースで作るJava+DB
第2回 コードから学ぶJSPのデータベースアクセスの基礎 (5/8)

パラメータクエリーを使う

 ところで、リスト2のプログラムは、セキュリティ上、少し問題がある。それは、ユーザーが不正な文字列を入力した場合だ。リスト2では、45行目にあるように、文字列の結合でINSERT文を利用している。

45: int resultCnt =
   stmt.executeUpdate(
   "INSERT INTO Keiji " +
   " (name, email, title, body, host) " +
   " VALUES ('" + username + "'," +
   " '" + email + "'," +
   " '" + title + "'," +
   " '" + msg + "'," +
   " '" + host + "')");

 SQLでは、文字列はシングルクォートで囲まなければならないという決まりがあるのだ。そこで、上記の文字列結合では、文字列の前後にシングルクォートを入れている。しかし、ユーザーが入力した元々の文字列にシングルクォートが含まれている場合には、それを「\'」または「''」(シングルクォート2つ)に置換するエスケープ処理をしなければならない。そうでなけれれば正しい書式と解釈されず、エラーとなってしまう。ここでエラーとなるだけならばよいが、もしも悪意あるユーザーがSQLコマンドを埋め込んでしまうことさえ可能だ。

 ユーザーが入力した文字列に対しては、不正な文字列が含まれていることを想定し、それをエスケープする処理を行わなければセキュリティホールになりうる。

 不正な文字列をエスケープするには、正規表現のパターンマッチで調査するという方法もある。しかしより容易且つ確実な方法として、文字列のエスケープをJDBC側の処理に任せてしまう方法でもよい。そのためには、パラメータクエリーと呼ばれる方法を使う。

パラメータクエリーとは

 パラメータクエリーとは、クエリーに対して、実行時に値を埋め込む方法だ。パラメータクエリーを使う場合には、値を埋め込みたい場所に、「?」文字を入れておく。実行するINSERT文は、たとえば次のようになる。

INSERT INTO Keiji(name, email, title, body, host)
VALUES(?, ?, ?, ?, ?)

 この「?」の部分を「パラメータ」と呼ぶ。実行時にパラメータの部分に何らかの値を(エスケープ処理をした上で)埋め込み、実行するというのがパラメータクエリーの仕組みだ。

 埋め込み処理はJDBCによって行なわれるため、開発者は埋め込みたい値を設定するだけでよい。パラメータに値を埋め込むことを「パラメータのバインド」と呼ぶ。具体的には、リスト2の37行目および、43〜45行目を、リスト3のように変更すればよい。

リスト3 パラメータクエリーに対応させたもの

37: PreparedStatement stmt = null;
38〜42: …中略…
43: // パラメータクエリーを準備する
44: stmt = conn.prepareStatement(
  "INSERT INTO Keiji " +
  " (name, email, title, body, host) " +
  " VALUES(?, ?, ?, ?, ?)");
45:
46: // パラメータに値を埋め込む
47: stmt.setString(1, username);
48: stmt.setString(2, email);
49: stmt.setString(3, title);
50: stmt.setString(4, msg);
51: stmt.setString(5, host);
52:
53: // 実行する
54: int resultCnt = stmt.executeUpdate();
55〜: …以下略…

 パラメータクエリーを使う場合には、Statementオブジェクトではなく、PreparedStatementオブジェクトを用いる。

 PreparedStatementオブジェクトは、44行目に示したように、ConnectionオブジェクトのprepareStatementメソッドの呼び出しによって得られる。

44: stmt = conn.prepareStatement(
   "INSERT INTO Keiji " +
   " (name, email, title, body, host) " +
   " VALUES(?, ?, ?, ?, ?)");

 prepareStatementメソッドの引数には、パラメータクエリーを指定する。

One Point:

prepareStatementメソッドの引数には、通常のクエリーを渡してもよい。つまり「?」をひとつも含まないクエリーを与えても構わない。その場合には、クエリーを渡す際に結局は、パラメータをひとつも利用しないことになる。このため、使い方はStatementオブジェクトとほぼ同じになるのだ。しかし、prepareStatementメソッドを使った場合には、prepareStatementメソッドを呼び出した時点で、クエリーの事前準備が行なわれるので、同じクエリーをなんども実行する場合には、高速に処理されるというメリットがある。ただしクエリーの事前準備をすると高速になるかは、利用しているJDBCドライバやデータベースエンジン次第だ。

 パラメータクエリーのパラメータ部分に、値を埋め込むには、PreparedStatementオブジェクトの“set型名”というメソッドを用いる。

46: // パラメータに値を埋め込む
47: stmt.setString(1, username);
48: stmt.setString(2, email);
49: stmt.setString(3, title);
50: stmt.setString(4, msg);
51: stmt.setString(5, host);

前のページへ | 1 2 3 4 5 6 7 8 | 次のページへ

[大澤文孝,ITmedia]