プロが教える店舗&オフィスのセキュリティ対策術

お世話になります。

jQueryのプラグインを使用してDBから吐き出したデータを並べ替えをして、
UPDATEをさせているのですが、UPDATE先のテーブル名を変数に置いた場合、

例えば、

$id_set = "1";

if (isset($_POST['result'])) {

$sql = "UPDATE `{$id_set}` SET no = ? WHERE id = ?";
$stmt = $conn->prepare($sql);

foreach (explode(',', $_POST['result']) as $i => $id) {
$stmt->execute(array(++$i, $id));

} //foreachの終了


} //if (isset($_POST['result'])) の終了

$sql1 = "SELECT id, name, price, no FROM `{$id_set}` ORDER BY no";
$stmt = $conn->prepare($sql1);
$stmt->execute();

foreach ($stmt as $row) {

$state[] = array("id"=>$row['id'], "name"=>$row['name'], "price"=>$row['price'], "no"=>$row['no']);
$this->smarty_obj->assign("state_loop",$state);

} //foreachの終了

とすれば、「 1 」というテーブルのデータをSELECTしてUPDATEと正常に走るのですが、

$id_set = $_POST["id"];

等としてpostした場合、UPDATEの際に「 Call to a member function execute 」のエラーが
吐かれます。該当のテーブルが存在しないと認識されているのだと思いますが、
ページ遷移してくる際に持ってきたデータを活用してテーブル名にセットして、
尚且つ正常にUPDATEさせるにはどの様な構文にすれば良いのでしょうか?

ご教示の程、どうぞよろしくお願い申し上げます。

----------------
▼スペック

PHP 5.3.3
MySQL 5.0.95
----------------

A 回答 (3件)

ご参考。


http://nanisiteruno.blog116.fc2.com/blog-entry-3 …

$id_set = $_POST["id"];

にした時に、出来上がった「$sql」の中身をデバッグプリントしてみよう。

たぶん「$id_set」に「貴方が想定してない文字列」が入って来ている筈。

例えば「URIエンコードされてて、入れた文字列そのままじゃない」とか「末尾に改行コードが居る」とか「文字化けしている」とか。
    • good
    • 0
この回答へのお礼

UPDATEした際に、$_POST["id"]の値が空になっていたのが原因でした。

前のページからデータを引っ張ってきているものの、同ページ(ファイル)で
idに値を投げていなので当然ですよね・・・。

ありがとうございました。参考になりました。

お礼日時:2013/12/21 04:04

わざわざセキュリティのためプリペアドで処理しているのに、


汚染されているPOSTデータをSQL文に取り込んでどうするの・・・

テーブル名のリストをつくって配列にいれて、受け取るのはテーブル名ではなく
数値などで受け取って該当するテーブル名を返すのが妥当

もちろん、in_arrayで該当する番号がない場合はSQLを実行しないような
分岐処理もわすれずに
    • good
    • 0
この回答へのお礼

ご回答をいただき、ありがとうございました。

参考になりました。

お礼日時:2013/12/21 04:05

>> 「 Call to a member function execute 」



PDOStatementの生成に失敗しています。$stmtの値はFALSEになっているはずです。PDOを使うときは例外をスローする設定にし、PDOExceptionをCatchできるようにコーディングを行うのが常識です。1回1回prepare()やexecute()の返り値がFALSEになっていないかチェックするのって馬鹿馬鹿しいでしょう、ここは例外に任せておけばラクになります。

PHPでデータベースに接続するときのまとめ
http://qiita.com/mpyw/items/b00b72c5c95aac573b71

PDO::ATTR_ERRMODE オプションの値を PDO::ERRMODE_EXCEPTION に設定するようにしてください。また、PDO::ATTR_EMULATE_PREPARES も FALSE にしておいたほうが基本的には正解です。型キャストまわりで気持ち悪い仕様が多いので。

生成に失敗する直接の原因ですが…

>> $sql = "UPDATE `{$id_set}` SET no = ? WHERE id = ?";

$id_set におかしな値が入っているからでしょうね。

>> $id_set = $_POST["id"];

絶対にやめてください。せっかくプリペアドステートメントを使ってるのに、POSTされた値を何の検証もせずしてテーブル名にそのままセットしてしまったらSQLインジェクション攻撃への対策が台無しになってしまいます。

既に他の回答者さんから提示がありますが、あらかじめ許可する値を

$id_sets = array('1', '2', '3');

として配列に用意しておき、

if (
____isset($_POST['result'], $_POST['id']) &&
____false !== $key = array_search($_POST['id'], $id_sets, true)
) {
____$id_set = $id_sets[$key];
}

とするなどの工夫が必要です。どうせキーが必要なら in_array よりは array_search を使っておいた方が正解でしょう。また、この前の自分の回答で手抜き仕様にしてしまって申し訳ないのですが、不正パラメータのチェックをもう少しちゃんと行ったほうがいいかもしれません。具体的には型の検証です。

$_GET, $_POSTなどを受け取る際の処理
http://qiita.com/mpyw/items/2f9955db1c02eeef43ea

単に配列が文字列として処理された場合には E_NOTICE を発生し文字列 "Array" に変化するだけで済むのですが、explodeに配列が渡されてしまった場合 E_WARNING を発生し、さらにその後のforeachまで E_WARNING を連続的に発生していろいろ問題が起きてしまうので、ここは厳密にチェックを行うべきだと思います。これらを考慮すると先ほどのifブロックは

isset($_POST['result'], $_POST['id']) &&
is_string($_POST['result']) &&
false !== $key = array_search($_POST['id'], $id_sets, true)

となりますね。

なお、「false !== 」を頭に持ってくるメリットしては、代入を同時に行う際に ( ) が1つ不要に点があるでしょう。

○ false !== ($key = array_search($_POST['id'], $id_sets, true))
○ false !== $key = array_search($_POST['id'], $id_sets, true)
○ ($key = array_search($_POST['id'], $id_sets, true)) !== false
× $key = array_search($_POST['id'], $id_sets, true) !== false

最後の例だと !== の方が優先順位が高いために、
「array_search($_POST['id'], $id_sets, true) !== false」
の結果の True/False が $key に代入されてしまいます。

「false !== 」が問題ないのは、もし「false !== $key」が先に評価されてしまったとしたら、変数ではなく「値」に対する代入を行うことになり、この文自体が無効になるからです。PHP4のあるバージョン以降からは無効になる文のことを考慮して優先度を変化させてくれるようになったようです。
    • good
    • 0
この回答へのお礼

前回に引き続き、大変恐縮です。

UPDATEをする際に、$id_setに値が空になっていました。
同ファイル内でUPDATEさせる際に値を投げていなかったので、
単純にテーブルが存在しないという事になっていた様です。


>foreachまで E_WARNING を連続的に発生していろいろ問題が起きてしまう

$_POST['result']が文字列かどうかのチェックをする必要があるということですね。
false !==を先に記述する意味も理解できました。

配列の格納から、値の検証まで一連の流れをご教示いただき、大変勉強になりました。

本当にありがとうございました。

お礼日時:2013/12/21 04:49

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!