--- title: プレースホルダを用いる author: kazu634 date: 2009-07-29 wordtwit_post_info: - 'O:8:"stdClass":13:{s:6:"manual";b:0;s:11:"tweet_times";i:1;s:5:"delay";i:0;s:7:"enabled";i:1;s:10:"separation";s:2:"60";s:7:"version";s:3:"3.7";s:14:"tweet_template";b:0;s:6:"status";i:2;s:6:"result";a:0:{}s:13:"tweet_counter";i:2;s:13:"tweet_log_ids";a:1:{i:0;i:4729;}s:9:"hash_tags";a:0:{}s:8:"accounts";a:1:{i:0;s:7:"kazu634";}}' categories: - Perl ---

PerlでDBを操作する際に、ユーザーからデータの入力を受け付けたい場面があります。例えば今回私が作成したウェブページでは住所を入力してもらい、それからクエリーを作成しています。こんな風に:


D

この際に、安易にコーディングしてしまうと、住所の入力を求めるテキストボックスエリアにSQL文を入力した場合に任意のSQL文が実行されてしまいます。それを防ぐためにプレースホルダという仕組みがあります。

このプレースホルダを使用すると、仮にSQL文が入力されても無害にしてくれる…そんな仕組みです。

SQL文

次のようにして事前に用いるSQL文を試してみます。

kazu634@kazu634-desktop% mysql -uroot -pmusashi                        ~/public_html/cgi-bin [3508]
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 93
Server version: 5.0.75-0ubuntu10.2 (Ubuntu)
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> use sample;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> SELECT address FROM renoir WHERE address LIKE '東京%';
+---------------------------------------------+
| address                                     |
+---------------------------------------------+
| 東京都豊島区東池袋1-40-2           |
| 東京都豊島区東池袋1-42-8           |
| 東京都豊島区巣鴨1-15-1              |
| 東京都文京区本郷4-37-13             |
| 東京都品川区大崎1-6-4               |
| 東京都中野区中野5-61-2              |
| 東京都中野区中野5-67-5              |
| 東京都中央区銀座2-7-18              |
| 東京都中央区銀座2-5-5               |
| 東京都中央区銀座2-8-15              |
| 東京都中央区銀座2-11-6              |
| 東京都中央区銀座3-3-11              |
| 東京都中央区銀座6-12-10             |
| 東京都中央区銀座5-15-1              |
| 東京都中央区日本橋2-3-6            |
| 東京都中央区日本橋本町1-1        |
| 東京都中央区八重洲1-6-17           |
| 東京都中央区八重洲1-7-4            |
| 東京都大田区大森北1-1-10           |
| 東京都大田区西蒲田8-1-7            |
| 東京都台東区上野6-1-1               |
| 東京都台東区上野4-10-7              |
| 東京都台東区上野2-13-13             |
| 東京都台東区谷中7-20-6              |
| 東京都千代田区五番町              |
| 東京都千代田区富士見2-2-6         |
| 東京都千代田区三崎町3-6-13        |
| 東京都千代田区内神田2-9-9         |
| 東京都千代田区内神田3-21-8        |
| 東京都千代田区鍛冶町2-1-4         |
| 東京都千代田区有楽町1-6-1         |
| 東京都千代田区外神田1-16-10       |
| 東京都千代田区外神田1-11-6        |
| 東京都千代田区神田佐久間町1-18 |
| 東京都杉並区阿佐ヶ谷南2-14-10    |
| 東京都杉並区高円寺北2-4-4         |
| 東京都新宿区西新宿1-24-1           |
| 東京都新宿区西新宿1-17-1           |
| 東京都新宿区西新宿1-5-11           |
| 東京都新宿区西新宿7-23-2           |
| 東京都新宿区新宿2-19-1              |
| 東京都新宿区新宿3-4-1               |
| 東京都新宿区市谷田町1-3           |
| 東京都新宿区四谷1-3-22              |
| 東京都新宿区歌舞伎町1-3-5         |
| 東京都新宿区歌舞伎町1-14-4        |
| 東京都新宿区歌舞伎町1-26-6        |
| 東京都新宿区西新宿7-1-1            |
| 東京都新宿区西新宿7-9-7            |
| 東京都新宿区百人町1-18-8           |
| 東京都新宿区百人町2-11-25          |
| 東京都新宿区高田馬場2-18-6        |
| 東京都新宿区高田馬場2-14-2        |
| 東京都新宿区高田馬場1-34-8        |
| 東京都新宿区高田馬場1-34-12       |
| 東京都渋谷区代々木1-30-6           |
| 東京都渋谷区千駄ヶ谷1-30-8        |
| 東京都渋谷区桜丘町15-15            |
| 東京都渋谷区渋谷2-17-5              |
| 東京都渋谷区宇田川町36-2          |
| 東京都渋谷区渋谷1-16-14             |
| 東京都荒川区西日暮里5-23-6        |
| 東京都荒川区東日暮里5-51-11       |
| 東京都港区赤坂3-10-2                 |
| 東京都港区新橋4-10-2                 |
| 東京都港区新橋1-17-2                 |
| 東京都港区芝5-34-7                    |
| 東京都港区芝大門2-3-1               |
| 東京都港区港南2-3-29                 |
| 東京都港区高輪3-25-22                |
| 東京都江東区亀戸2-20-7              |
| 東京都葛飾区東金町1-42             |
| 東京都立川市柴崎町3-4-14           |
| 東京都立川市曙町2-13-10             |
| 東京都立川市曙町2-9-1               |
| 東京都武蔵野市中町1-6-7            |
+---------------------------------------------+
76 rows in set (.00 sec)
mysql> exit
Bye

Perlのソース

#!/usr/bin/env perl
# === Libraries ===
use strict;
use warnings;
use Data::Dumper;
use CGI;
use CGI::Carp;
use DBI;
# === Main part ===
my $q = new CGI;
my $query = $q->param('adr') || '東京';
# データベースへの接続
my $dbh =
DBI->connect( 'dbi:mysql:dbname=sample', 'root', 'musashi',
{ RaiseError => 1, AutoCommit =>  } );
# ステートメントハンドラの作成
# ここでプレースホルダを指定している
my $sth = $dbh->prepare("SELECT address FROM renoir WHERE address LIKE ?;");
# ステートメントハンドラの実行
# プレースホルダに入れ込む変数を指定している
$sth->execute("$query%");
print("Content-type: text/html; char-set=utf-8\n\n");
print("<table width=\"\" cellspacing=\"\" cellpadding=\"\" border=\"\">\n");
print("<tr>\n<th>住所</th>\n</tr>\n");
while ( my @row = $sth->fetchrow_array ) {
print "<tr>\n<td>@row</td>\n</tr>\n";
}
print("</table>");
# ステートメントハンドラの解放
$sth->finish;
# データベースハンドラの解放
$dbh->disconnect;

htmlのソース

<html>
<head>
    <title>DB test</title>
    <meta name="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="Content-Script-Type" content="text/javascript" />
  </head>
<body>
<style type="text/css">
html, body {
width: 100%;
height: 100%;
}
html {
overflow: hidden;
}
body {
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
}
#mymap {
margin-bottom: 80%;
height: 25%;
width 25%;
}
#bottompanel {
position: absolute;
left: 0px;
border solid;
bottom: 0px;
height: 75%;
width: 25%;
overflow: auto;
padding: 0px 5px 0px 10px;
}
</style>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript">
//<![CDATA[
function lwws() {
// 結果の表示先に「しばらくお待ちください」と表示する
document.getElementById("result").innerHTML = "<p>しばらくお待ちください...</p>";
new Ajax.Updater(
"result", "cgi-bin/mysql.pl",
{
parameters : Form.serialize("ajax"),
method : "post"
}
);
}
//]]>
    </script>
<div id="mymap">
<h1>DB test</h1>
<form method="post" id="ajax" action="" name="ajax">
住所: <input type="text" name="adr">
<input type="button" value="送信" onclick="lwws()">
</form>
</div>
<div id="bottompanel">
<div id="result"></div>
</div>
</body>
</html>

まとめ

prepareの部分で「?」を用いましょう!