我啥时候应该使用准备好的语句?

Posted

技术标签:

【中文标题】我啥时候应该使用准备好的语句?【英文标题】:When should I use prepared statements?我什么时候应该使用准备好的语句? 【发布时间】:2014-09-19 06:20:30 【问题描述】:

原来我用mysql_connectmysql_query做事。然后我了解了SQL注入,所以我正在尝试学习如何使用准备好的语句。我了解 PDO 类的准备和执行功能如何有助于防止 SQL 注入。

是否仅当用户输入存储到数据库中时才需要准备好的语句?仍然使用mysql_num_rows 可以吗,因为我并没有真正冒着使用此功能被黑客入侵的风险?还是使用准备好的语句来执行此操作更安全?对于涉及使用 MySQL 的所有内容,我应该使用准备好的语句吗?为什么?

【问题讨论】:

如果你能保证你的代码库中没有任何部分使用过用户生成的查询数据,那么就没有意义了。如果您甚至有一个查询,那么使用两种不同的查询方式是没有意义的。首先使用准备好的查询以确保安全性,其次才是一致性。 你好像有点困惑。首先请don't use mysql_*mysql_* 函数已过时、deprecated 且不安全。请改用MySQLiPDO。其次,mysql_num_rows 与准备好的语句无关,也不是 PDO 功能。在运行查询之前准备语句,而不是在要计算行数时准备语句。 【参考方案1】:

tl/dr

总是。 100% 的时间,使用它。总是;即使你不需要使用它。仍然使用它。


mysql_* 函数已弃用。 (Notice the big red box?)

警告 此扩展在 php 5.5.0 中已弃用,并已被删除 在 PHP 7.0.0 中。相反,MySQLi 或 PDO_MySQL 扩展名应该是 用过的。另请参阅MySQL: choosing an API 指南和related FAQ 了解更多信息 信息。此功能的替代方案包括:

mysqli_connect() PDO::__construct()

您最好使用PDOMySQLi。当使用准备好的语句时,这些 2 中的任何一个都足以作为兼容库。

在没有准备好的陈述/对其进行消毒的情况下信任用户输入,就像将您的汽车放在一个糟糕的社区,解锁并且钥匙在点火装置中。你基本上是在说,进来拿我的好东西

您应该从不,我的意思是永远不要相信用户输入。除非你想要这个:

关于数据的参考和存储,如 cmets 中所述,您可以永远也不应该相信任何与用户相关的输入。除非您 101% 确定用于操作所述数据库/值的数据已硬编码到您的应用中,否则您必须使用准备好的语句。

现在谈谈为什么应该使用准备好的语句。这很简单。防止 SQL 注入,但尽可能以最直接的方式。准备好的语句的工作方式很简单,它将 querydata 一起发送,但分开发送(如果这有意义哈哈) - 我的意思是这样的:

Prepared Statements
Query: SELECT foo FROM bar WHERE foo = ?
Data:  [? = 'a value here']

与其前身相比,您使用数据截断查询,将其作为一个整体发送 - 反过来,这意味着它作为单个事务执行 - 导致 SQL 注入漏洞。

这是一个伪 PHP PDO 示例,向您展示准备好的语句/绑定的简单性。

$dbh = PDO(....); // dsn in there mmm yeahh
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);

// insert one row
$name = 'one';
$value = 1;
$stmt->execute();

Taken from PHP Manual for PDO Prepared Statements


更多阅读

How can I prevent SQL-injection in php? What is SQL-injection? (Simple Terms)

【讨论】:

@Darren 我想知道我是否做对了。是否有必要在只有固定值的情况下使用准备好的语句?例如,有人需要批准或拒绝请求,而该人获得的唯一输入是选择 2 个按钮批准和拒绝,每个按钮写入不同的值,这些值是固定的。我需要在那里使用准备好的声明吗? 注意: 该页面不允许用户输入任何文本。 @BRoebie 安全总比后悔好。用户可以与传递的数据(即表单)进行交互的任何地方基本上都应该是准备好的语句。如果您从输入中获取所述值,而这些值很容易通过元素检查等进行修改,那么您可能会遇到很多麻烦。 @Darren 我同意。我想知道如果用户只能与页面上的 2 个按钮交互,我是否需要使用准备好的语句i> 在数据库中。 (是的,这是我正在谈论的一种形式,但所有字段都是只读字段,无法修改,只有我上面提到的 2 个按钮,代码中的固定值发送到数据库)跨度> 我知道这已经很晚了(非常痛苦),但我只想提一下,在谈到“用户输入”时,这意味着发送到您的页面/应用程序的请求,而不是具体的一个网络表单。用户可以直接向您的应用程序发出 HTTP 请求(例如),而无需实际请求您网站上的索引(或任何其他相关)页面。请了解服务器和客户端之间的通信是如何工作的,因为这很关键?【参考方案2】:

对此有两种解决方案-

01- 使用准备好的语句

为了防止 SQL 注入,我们将不得不使用一种称为预处理语句的东西,它使用绑定参数。 Prepared Statements 不会将变量与 SQL 字符串结合起来,因此攻击者无法修改 SQL 语句。 Prepared Statements 将变量与编译后的 SQL 语句结合起来,这意味着 SQL 和变量是分开发送的,变量只是被解释为字符串,而不是 SQL 语句的一部分。

02- 使用 mySQLi 准备语句。

使用以下步骤中的方法,您将不需要使用任何其他 SQL 注入过滤技术,例如 mysql_real_escape_string()。这是因为使用prepared statements 不可能进行传统的SQL注入。

例如 -

$name = $_GET['username'];

if ($stmt = $mysqli->prepare("SELECT password FROM tbl_users WHERE name=?")) 

    // Bind a variable to the parameter as a string. 
    $stmt->bind_param("s", $name);

    // Execute the statement.
    $stmt->execute();

    // Get the variables from the query.
    $stmt->bind_result($pass);

    // Fetch the data.
    $stmt->fetch();

    // Display the data.
    printf("Password for user %s is %s\n", $name, $pass);

    // Close the prepared statement.
    $stmt->close();


您可以找到有关此表单的更多信息 - http://www.wikihow.com/Prevent-SQL-Injection-in-PHP

【讨论】:

【参考方案3】:

TL;DR 如果您的应用接受任何用户输入,则 100% 使用准备好的语句


你似乎有点困惑。首先请don't use mysql_*mysql_* 函数已过时、deprecated 且不安全。请改用MySQLiPDO。其次,mysql_num_rows 与准备好的语句无关,也不是 PDO 功能。在运行查询之前准备语句,而不是在要计算行数时准备语句。

至于何时准备声明,@Mike'Pomax'Kamermans 将其钉在了 cmets 中。如果您曾经(甚至曾经)使用过任何用户(甚至是所谓的受信任用户)接触过的数据,或者由任何类型的第三方或第三方应用程序(包括浏览器)生成的数据,请使用准备好的语句。只有当您的数据 100% 是硬编码或完全由您的代码生成(如简单的计数器变量)时,您才能信任它。

例如,你不能信任:

用户名 密码 电子邮件地址 用户cmets 电话号码 日期 搜索字符串 浏览器客户端字符串 信用卡号码 上传文件名 以及任何用户创建的或用户可以操作的其他类型的输入。

当然,在将它们放入数据库之前,您应该验证所有这些(例如,检查电子邮件地址是否真的是电子邮件地址)。但即便如此,使用准备好的语句也是安全的方法。

【讨论】:

我明白你在说什么。我提到我使用 mysql_num_rows 的原因是因为我看到了这个***.com/questions/11305230/…,这似乎毫无意义。如果没有处理用户数据,那么 mysql_num_rows 应该没问题吧? 是的;只要您确定没有用户数据,例如SELECT COUNT(*) FROM mytable,您就不必使用准备好的语句。不过,就个人而言,我建议始终使用准备好的语句;当没有用户数据的旧查询被更新并且程序员忘记为新查询使用准备好的语句时,会引入许多安全漏洞。这只是我的看法。【参考方案4】:

Mysql_* 已被弃用,因此最好切换mysqli_*PDO

为了防止 sql 注入 (mysql) :- How can I prevent SQL injection in PHP?.

和准备好的语句(这些是与任何参数分开发送到数据库服务器并由数据库服务器解析的 SQL 语句。)用于您的每个用户生成的查询数据。

喜欢通过查询将匹配/获取记录的数据发布到数据库。所以意思是当您使用表单数据触发查询时。

【讨论】:

以上是关于我啥时候应该使用准备好的语句?的主要内容,如果未能解决你的问题,请参考以下文章

我啥时候应该使用结构而不是类?

我啥时候应该使用一对一的关系?

我啥时候应该考虑使用 ORM 框架?

函子我啥时候应该使用它们它们的预期用途是啥[关闭]

什么时候在 PHP Mysqli 中使用准备好的语句? - 用户表单搜索输入与选择查询

我啥时候应该将 layer.shouldRasterize 设置为 YES