PHP& MySQL:准备语句与函数的安全性和编码速度

时间:2016-05-07 15:43:51

标签: php function mysqli prepared-statement

这更像是一个“最佳实践”的问题。真。

目前编写一个网站后端,并听取了使用mysqli准备语句的强烈建议,所以一直这样做。

然而,在过去,我也被告知格言“更聪明,而不是更难”,并且我从HTML表单中获取数据,并将每个输入的名称作为其字段名称并且已处理的数据位于具有相同字段名称的数组中,现在遇到了一些困惑。

我有这个代码,我过去曾用它将数据从数组放入数据库,更新为使用mysqli-> escape_string()而不是mysql_real_escape_string():

function create_insert_from_array($array, $table) {
  global $mysqli;

  $part_one = ""; $part_two = "";

  foreach ($array as $field => $input) {
    $part_one .= "`" . $mysqli->escape_string($field) . "`, ";
    $part_two .= "'" . $mysqli->escape_string($input) . "', ";
  }

  $to_ret = "INSERT INTO `" . $table . "` (" . substr($part_one, 0, -2) . ") VALUES ("
          . substr($part_two, 0, -2) . ");";

  return $to_ret;

} 

这允许我放入它进入的数组和表中,然后弹出一个SQL语句。

一个表的My SQL Prepared Statement方法使用以下代码:

$stmt = $mysqli->prepare("INSERT INTO aio_sched_programmes
                          VALUES ('', ?, ?, ?, ?, ?, ?, ?, UNIX_TIMESTAMP(), ?, ?, '', '', '');");
$stmt->bind_param('sssssssis',
                    $end_data['name'],
                    $url_slug,
                    $end_data['long_desc'],
                    $end_data['short_desc'],
                    $end_data['image_url'],
                    $end_data['twitter'],
                    $end_data['facebook'],
                    $_SESSION['user_id'],
                    $user_profile['name']    );

if ($stmt->execute()) {
  // DO SOME STUFF AFTER CODE EXECUTION
}

我现在遇到的问题是,在特定的准备声明中,所有字段都是必需的。但是,在我正在处理的表中,并非所有这些都是,并且在我的数组中,有些将不存在,因此当我的函数创建语句时将被忽略。但是,使用准备好的语句,所有字段都需要放入语句中,并带有可能的空格。

我已经向我(主要来自这个网站!)预先准备好的语句更加安全,但是我的函数会节省很多编码,因为我可以调用它而不必写出准备好的语句

那么,哪个更好用,或者是否有任何方式可以将这两个结合起来以实现两全其美?

2 个答案:

答案 0 :(得分:-1)

只要在语句中涉及外部数据,就应尽可能使用预准备语句。由于在INSERT INTO上未提及的字段设置为其默认值,因此您也可以在PHP中提供该值。使用默认值和参数化SQL保存数组。如果必须插入缺少条目的数据,请将其合并为默认值。

<?php

function store_data(Array &$data)
{
  $default_data =
  [
    'street'  => null,
    'city'    => null,
    'credits' => 0
  ];

  $dat = array_merge($default_data, $data);
  var_dump($dat);
}

$data = 
[
  'city' => 'New York'
];
store_data($data);
?>

输出:

array (size=3)
  'street' => null
  'city' => string 'New York' (length=8)
  'credits' => int 0

正如你在评论中所述:

如果在执行查询时不知道列,则不应依赖http请求输入。使用SHOW FULL COLUMNS FROM `tablename` FROM `db`;获取名称,类型,默认值和其他信息。这些字符串更安全,但不安全&#34;。你仍然需要对这些字符串进行替换,因为它们在法律上可能包含特殊的字符。 要获取表名,请使用SHOW TABLE FROM `db`;

答案 1 :(得分:-2)

准备语句的整个想法是保持一个&#34;数据库查询&#34;模板,将其发送一次到数据库,然后只发送参数。正如您在bindParam定义(http://php.net/manual/pl/pdostatement.bindparam.php)中所发现的那样,值被作为对变量的引用传递,因此您可以执行以下操作:

$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) 
VALUES (:firstname, :lastname, :email)");
$stmt->bindParam(':firstname', $firstname);
$stmt->bindParam(':lastname', $lastname);
$stmt->bindParam(':email', $email);

// insert a row
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();

// insert another row
$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();

(例如:http://www.w3schools.com/php/php_mysql_prepared_statements.asp)。

它可能会简化代码,但是大型应用程序中的imho会造成更大的混乱。

准备好的语句的一部分是你创建整个查询一次而不是字符串连接(这确实很容易出错)。最常见的问题之一是当你连接字符串和变量为空时,你会得到类似的东西:

INSERT INTO table ...... VALUES (1, 12, , 'ABC');

MySQL会给出语法错误(因为,,)。当人们进行查询字符串连接时,这是一个非常常见的问题。

另一个很棒的功能是,当您将参数绑定到查询时,您可以指定参数类型(我强烈建议您使用它)。使用这种方法,您可以轻松地支持任何数据库,例如,如果列position具有类型int并且您推送字符串:

INSERT INTO `myTable` (id, position) VALUES (1, '1');

MySQL将执行命令,PostgreSQL将失败,因为您尝试将string分配给整数。指定类型也解决了所有SQL注入问题。我在第一个例子中看到你正在逃避字符串(非常好),但很容易忘记这件小事。如果有人通过你已经逃脱了字符串怎么办?

恕我直言,如何创建数据库查询的最佳方法是   - 使用预备语句(更好的性能),更不容易出错   - 键入每个查询参数

好的代码示例(从php.net网站上窃取):

<?php
/* Execute a prepared statement by binding PHP variables */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);
$sth->execute();
?>

关于代码的一小部分提示,而不是使用foreach创建字符串,然后删除有关join()函数的最后一个参数。