5

好吧,我还是不太明白。我一直在阅读,为了正确逃避您的 MySQL 查询,您需要使用mysqli_prepare()and mysqli_bind_param()

我尝试使用此设置,坦率地说,它有点笨拙。当我不需要再次引用它们时,我会通过引用传递变量,而完成相同任务只是更多的代码行。

我想我只是不明白两者之间有什么区别:

<?php
$sql = new \MySQLi(...);
$result = $sql->query('
   UPDATE `table`
   SET
      `field` = "'.$sql->real_escape_string($_REQUEST[$field]).'";
');
?>

<?php
$sql = new \MySQLi(...);
$stmt = $sql->prepare('
   UPDATE `table`
   SET
      `field` = ?;
');
$value = $_REQUEST[$field];
$stmt->bind_param('s', $value);
$stmt->execute();
$result = $stmt->get_result();
unset($value);
?>

除了更多的代码。

我的意思是,他们是否实现了这一点,以便人们在发送查询之前不会忘记转义值?还是以某种方式更快?

或者当我打算重复使用同一个查询(因为 amysqli_stmt可以重复使用)并在其他情况下使用传统方法时,我应该使用这种方法吗?

4

4 回答 4

3

What you are reading, that you need to use mysqli_prepare() and mysqli_bind_param() functions to "properly escape your MySQL queries" is wrong.

It is true that if you use mysqli_prepare() and mysqli_bind_param(), you needn't (and shouldn't) "escape" the values supplied as bind parameters. So, in that sense, there's some truth in what you are reading.

It's only when unsafe variables are included in the SQL text (the actual text of the query) that you need to "properly escape" the variables, usually by wrapping the variables in mysqli_real_escape_string() function calls.

(We note that it's possible to make of use of prepared statements and still include un-escaped variables in the SQL text, rather than passing the variable values as bind_parameters. That does sort of defeats the purpose of using prepared statements, but the point is, either way, you can write code that is vulnerable.

MySQL now supports "server side" prepared statements (if the option is enabled in the connection), and that's a performance optimization (in some cases) of repeated executions of identical SQL text. (This has been long supported in other databases, such as Oracle, where making use of prepared statements has been a familiar pattern for, like, since forever.)

Q: Did they implement [prepared statements] so that people wouldn't forget to escape values before sending them in a query?

A: Based on the number of examples of code vulnerable to SQL Injection when not using prepared statements, despite the documentation regarding mysql_real_escape_string() function, you'd think that certainly would be sufficient reason.

I think one big benefit is that when we're reading code, we can see a SQL statement as a single string literal, rather than a concatenation of a bunch of variables, with quotes and dots and calls to mysql_real_escape_string, which isn't too bad with a simple query, but with a more complex query, it is just overly cumbersome. The use of the ? placeholder makes for a more understandable SQL statement,... true, I need to look at other lines of code to figure out what value is getting stuffed there. (I think the Oracle style named parameters :fee, :fi, :fo, :fum is preferable to the positional ?, ?, ?, ? notation.) But having STATIC SQL text is what is really the benefit.

Q: Or is it somehow faster?

As I mentioned before, the use of server side prepared statements can be and advantage in terms of performance. It's not always the case that it's faster, but for repeated execution of the same statement, where the only difference is literal values (as in repeated inserts), it can provide a performance boost.

Q: Or should I use this method when I intend to use the same query repeatedly (since a mysqli_stmt can be reused) and use the traditional method in other cases?

That's up to you. My preference is for using STATIC SQL text. But this really comes from a long history of using Oracle, and using the same pattern with MySQL fits naturally. (Albeit, from Perl using the DBI interface, and Java using JDBC and MyBATIS, or other ORMs (Hibernate, Glassfish JPA, et al.)

Following the same pattern just feels natural in PHP; the introduction of mysqli_ and PDO are a welcome relief from the arcane (and abused) mysql_ interface.

Good code can be written following either pattern. But I challenge you to think ahead, about more complex SQL statements, and whether the choice to use mysqli_real_escape_string() and concatenating together a dynamic string to be executed, rather than using static SQL text and bind parameters, might make reading, and deciphering, the actual SQL being executed more complicated for the soul that finds themselves maintaining code they didn't write.

I think studies have shown that code is read ten times more than it is written, which is why we strive to produce readable, understandable code, even if that means more lines of code. (When each statement is doing a single identifiable thing, that's usually easier for me to understand than reading a jumble of concatenated function calls in one complicated statement.

于 2013-09-06T00:18:48.247 回答
2

我发现写得很好有几个主要好处:

  1. 尽管该语句被执行多次,但编译和优化语句的开销只发生一次。并非所有优化都可以在编译准备好的语句时执行,原因有两个:最佳计划可能取决于参数的具体值,以及最佳计划可能会随着表和索引的变化而变化。
  2. 准备好的语句对 SQL 注入具有弹性,因为稍后使用不同协议传输的参数值不需要正确转义。如果原始语句模板不是来自外部输入,则不会发生 SQL 注入。

另一方面,如果一个查询只执行一次,服务器端准备好的语句可能会因为到服务器的额外往返而变​​慢。实施限制也可能导致性能损失:某些版本的 MySQL 没有缓存准备查询的结果,而某些 DBMS(如 PostgreSQL)在执行期间没有执行额外的查询优化。

资源

于 2013-09-05T23:42:56.830 回答
2

我认为与鼓励逻辑分离相比,后一种方法本身更安全不是问题。使用准备好的语句,SQL 查询独立于我们使用的值。这意味着,例如,当我们返回并更改查询时,我们不必将一堆不同的值连接到一个字符串,并且可能会冒忘记转义输入的风险。使代码更易于维护!

于 2013-09-05T23:33:35.740 回答
0

I would like to add that mysqli_bind_param() has been removed as of PHP 5.4.0. You should use mysqli_stmt_bind_param()

于 2013-09-05T23:54:52.313 回答