DB=> select * from テーブル名 where id = ' ' or 1=1;-- ' and pw = '';
データベースが解釈可能な文字列「'」「;」「--」によって、常に一致する条件
(1=1)を where 句に潜り込ませれば、テーブルの全ての行が条件に一致する。
SQL 文では、「--」以降はコメントとして扱われる。
・以下の対策を行う事で、SQL インジェクションは 100% 防げる。
1. apache モジュール(mod_security)による、送信データのチェック 2. プログラム内で、送信データのチェック 3. SQL 文で、プリペアードクエリを使用 実行する SQL 文を予めデータベース・サーバに登録し、実行時にパラメータ(値) のみ渡す。 パラメータは、SQL 文への変数として安全に処理されるため SQL インジェクション を行う事は不可能。 quote を使用 文字列を quote すると、'(シングルクォート)で囲まれる。 my $str = $dbh->quote( 値 ); my $sql = "insert into テーブル名( 列名, ... ) values( $str, ... )"; または、プレースフォルダを使用 ?(プレースフォルダ)を使用すると、値を埋め込む際に自動的に quote に相当 する処理が行われる。 my $sql = "insert into テーブル名( 列名, 列名, ... ) values( ?, ?, ... )"; my $sth = $dbh->prepare( $sql ); $sth->execute( 値, 値, ... );
my $sth = $dbh->prepare( SQL 文 ); 得られたステートメント・ハンドルにて、SQL 文の実行、検索結果の取得を行う。・戻り値
成功時 : ステートメント・ハンドル 失敗時 : undef
my $ret = $sth->execute();・戻り値
成功時 : 影響のあった行数 失敗時 : undef
insert, update, delete 時には、必ずトランザクションを使用する。
$dbh->begin_work; # トランザクション開始 my $sql = "SQL 文"; my $sth = $dbh->prepare( $sql ); # 実行準備 my $ret = $sth->execute( ); # 実行(戻り値は、影響のあった行数) $sth->finish; # ステートメント・ハンドルの開放 if ( $ret ) { # 成功時 $dbh->commit; # 確定 } else { # 失敗時 $dbh->rollback; # 破棄 }
my @array = $sth->fetchrow_array; 成功時 : 検索結果の1レコードを格納した配列 失敗時 : undef 使用例 my @data = ( ); while ( my ( $a, $b ) = $sth->fetchrow_array ) { my %hash = ( ); $hash{ a } = $a; $hash{ b } = $b; ※ fetchrow_hashref を使用すると上記を省略できる push @data, \%hash; }・fetchrow_hashref(中速)
my $hash_ref = $sth->fetchrow_hashref; 成功時 : 検索結果の1レコードを格納したハッシュのリファレンス 失敗時 : undef 使用例 my @data = ( ); while ( my $hash_ref = $sth->fetchrow_hashref ) { push @data, $hash_ref; }・bind_colums + fetchrow_arrayref(高速)
$sth->bind_colums( \$bind_var, \$bind_var, ... ); \$bind_var : バインドを行う変数のリファレンス バインドとはデータと変数を直接結びつけることで、データが取得されると 対応する変数の値が自動的に更新される。 データを取得するには、fetchrow_arrayref を呼び出す。 my $array_ref = $sth->fetchrow_arrayref; 成功時 : 検索結果の1レコードを格納した配列のリファレンス 失敗時 : undef 使用例 my %hash = ( ); my @data = ( ); $sth->bind_columns( undef, \( @hash{@{$sth->{NAME_lc}}} ) ); while ( $sth->fetchrow_arrayref ) { push @data, {%hash}; # { } で囲む、\ だと全て同じになる } 配列のスライスを使用して、ハッシュを作成 @hash{@{$sth->{NAME_lc}}} # @hash{ qw( 列名, 列名 ) } @{$sth->{NAME_lc}} # 列名のリスト $sth->{NAME_lc} # 列名(小文字)の取得・ベンチマークテスト
#!/usr/bin/env perl # 検索結果取得のベンチマークテスト use strict; use warnings; use v5.24; use Carp qw( croak ); use DBI; use Benchmark qw( cmpthese ); # データベースへの接続 my $dbname = 'データベース名'; my $ds = 'DBI:Pg:dbname=' . $dbname; # データソース my $user = 'ユーザー名'; my $pw = 'パスワード'; my $dbh = DBI->connect( $ds, $user, $pw, { RaiseError => 0, PrintError => 1, AutoCommit => 1 } ) or croak qq{can't connect : $!}; # SQL 文 my $sql = "select no, reg_day, name from member_tbl"; # 実行準備 my $sth = $dbh->prepare( $sql ); # fetchrow_array sub get1 { # 実行 $sth->execute(); # 検索結果の取得 my ( $no, $reg_day, $name ) = $sth->fetchrow_array; } # fetchrow_hashref sub get2 { # 実行 $sth->execute(); # 検索結果の取得 my $hash_ref = $sth->fetchrow_hashref; } # bind_columns + fetchrow_arrayref sub get3 { # 実行 $sth->execute(); # 検索結果の取得 my %hash = ( ); $sth->bind_columns( undef, \( @hash{@{$sth->{NAME_lc}}} ) ); $sth->fetchrow_arrayref; } # ベンチマークテストを十万回行う cmpthese ( 100000, { fetchrow_array => sub { get1( ) }, fetchrow_hashref => sub { get2( ) }, 'bind_columns + fetchrow_arrayref' => sub { get3( ) }, } ); # ステートメント・ハンドルの開放 $sth->finish; # データベースとの切断 $dbh->disconnect;・ベンチマークテストの結果
Rate | bind_columns ... | fetchrow_hashref | fetchrow_array | |
---|---|---|---|---|
bind_columns ... | 11364/s | -- | -10% | -51% |
fetchrow_hashref | 12579/s | 11% | -- | -46% |
fetchrow_array | 23202/s | 104% | 84% | -- |
全ての処理が終わったら、ステートメント・ハンドルを開放する。 $sth->finish;
$dbh->disconnect;