提问人:G.SINGH 提问时间:7/28/2014 最后编辑:DharmanG.SINGH 更新时间:11/13/2023 访问量:18584
什么时候应该使用预准备语句?
When should I use prepared statements?
问:
本来我用和做事。然后我学习了SQL注入,所以我正在尝试学习如何使用预准备语句。我了解 PDO 类的准备和执行函数如何有助于防止 SQL 注入。mysql_connect
mysql_query
是否只有在将用户输入存储到数据库中时才需要准备好的语句?仍然可以使用,因为我真的没有使用此功能被黑客入侵的风险吗?还是使用准备好的语句来执行此操作更安全?对于涉及使用MySQL的所有内容,我是否应该使用预准备语句?为什么?mysql_num_rows
答:
Mysql_*
已被弃用,因此最好切换或mysqli_*
PDO
为了防止SQL注入(mysql):-如何防止PHP中的SQL注入?.
和预准备语句(这些是与任何参数分开发送到数据库服务器并由数据库服务器解析的 SQL 语句)用于每个用户生成的查询数据。
就像在发布数据时一样,您将记录与查询匹配/获取到数据库。所以意味着当你用表单数据触发查询时。
TL;DR 如果您的 SQL 使用任何类型的数据或输入,则 100% 的时间使用准备好的语句
你似乎有点困惑。首先,请不要使用mysql_*
;这些函数已过时、已弃用且不安全。请改用 MySQLi
或 PDO
。其次,它与准备好的语句无关,无论如何都不是 PDO 功能。在运行查询之前准备语句,而不是在运行查询时准备行数。mysql_*
mysql_num_rows
至于何时准备发言,@Mike'Pomax'Kamermans在评论中指出了这一点。如果您曾经使用过任何数据,即使是一次,使用用户(甚至是据称受信任的用户)曾经接触过的任何数据,或者由任何类型的第三方或第三方应用程序(包括浏览器)生成的数据,请使用准备好的语句。只有当你的数据100%是硬编码的,你才能信任它。
例如,您不能信任:
- 用户名
- 密码
- 电子邮件地址
- 用户评论
- 电话号码
- 日期
- 搜索字符串
- 浏览器客户端字符串
- 信用卡号码
- 上传的文件名
- 以及由用户创建或用户可以操作的任何其他类型的输入。
当然,在将它们放入数据库之前,您应该验证所有这些(例如,检查电子邮件地址是否确实是电子邮件地址)。但即便如此,使用准备好的语句也是安全的方法。
评论
SELECT COUNT(*) FROM mytable
TL/DR
总是。100%的时间,使用它。总是;即使你不需要使用它。仍然使用它。
mysql_*
函数已失效,因此您必须使用 PDO
或 MySQLi
。使用预准备语句时,这 2 个中的任何一个都足以作为兼容库。
直接在 SQL 查询中添加变量就像把你的车停在一个糟糕的街区,解锁并按下点火开关的钥匙。你基本上是在说,进来拿走我的好东西
你永远不应该,我的意思是永远不要,把一个数据变量放到SQL中。 除非你想要这个:
您还应该避免任何模糊的定义,例如“用户输入”。我们不能严格定义什么是“用户输入”。更不用说任何输入都可能包含会破坏您的查询的愚蠢引用。重要的不是数据来自哪里,而是数据的来源:任何数据都将在 SQL 中使用?通过准备好的语句传递它。
现在来看看它是如何工作的。准备好的语句的工作方式很简单,它将查询和数据一起发送,但分开(如果这有意义的哈哈) - 我的意思是:
Prepared Statements
Query: SELECT foo FROM bar WHERE foo = ?
Data: [? = 'a value here']
与其前身相比,您在查询中填充了数据,将其作为一个整体发送 - 反过来,这意味着它是作为单个事务执行的 - 导致 SQL 注入漏洞。
鉴于您需要在查询中使用以下变量,
$name = 'one';
$value = 1;
对于 PDO,可以使用如下代码
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?,?)");
$stmt->execute([$name, $value]);
对于 mysqli,
$dbh->execute_query("INSERT INTO REGISTRY (name, value) VALUES (?,?)", [$name, $value]);
更多阅读
评论
对此有两种解决方案——
01- 使用准备好的语句
为了防止 SQL 注入,我们将不得不使用一种称为预处理语句的东西,它使用绑定参数。预准备语句不会将变量与 SQL 字符串组合在一起,因此攻击者无法修改 SQL 语句。预准备语句将变量与编译的 SQL 语句组合在一起,这意味着 SQL 和变量是分开发送的,变量只是被解释为字符串,而不是 SQL 语句的一部分。
02-使用mySQLi准备语句。
使用以下步骤中的方法,您将不需要使用任何其他 SQL 注入过滤技术,例如 mysql_real_escape_string()。这是因为使用预准备语句无法进行常规的 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
使用真正准备好的语句意味着在添加任何数据之前将对 SQL 进行解释。
使用预准备语句有两个原因:
- 安全性:如果语句已经被解释过,那么数据被误解为 SQL 为时已晚。误解 SQL 数据可能会导致 SQL 注入攻击。
- 效率:如果你有一个语句,你需要用不同的数据多次运行,那么简单地将新数据注入到同一个SQL语句中并重新解释是低效的。如果先准备语句,然后使用不同的数据运行准备好的语句,则更有意义。
关于SQL注入,如果数据来自外部,例如来自表单,则应始终不信任数据。这不一定是您担心的攻击。键入错误或用作撇号的单引号可能会导致 SQL 错误。
但是,仅当数据为字符串时,才会发生 SQL 注入。如果通过诸如 之类的函数运行数据,则会清除任何可能被误解的字符串数据,因此在毫无准备的情况下使用是安全的。intval()
如果你的SQL纯粹来自你自己的代码,那么就没有必要变得偏执。如果你想破坏自己的数据库,有比形成SQL注入攻击更简单的方法。
但是,不要忘记效率方面:如果您多次运行相同的语句,那么,无论数据源如何,准备好的语句都可能更有效率。
还有一点。准备好的语句并不总是由数据库软件真正准备的。PHP 能够伪造准备好的语句。
使用 PDO 时,我总是建议以下几点:
$pdo = new PDO(…);
$pdo -> setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
特别是对于MySQL,这会导致PDO不会伪造准备好的语句。
评论
$rows = $pdo -> query('SELECT count(*) FROM data') -> fetchColumn();
foreach($pdo -> query('SELECT id, name FROM data') AS [$id,$name]) …
$pdo -> exec('TRUNCATE TABLE data');
exec()
query()
$id = intval($_POST['id']); $pdo -> query("SELECT * FROM data WHERE id=$id");
评论
mysql_*
;这些函数已过时、已弃用且不安全。请改用MySQLi
或PDO
。其次,它与准备好的语句无关,无论如何都不是 PDO 功能。在运行查询之前准备语句,而不是在运行查询时准备行数。mysql_*
mysql_num_rows