Php 用PDO和准备好的语句替换mysql_*函数
我一直都在做简单的连接,Php 用PDO和准备好的语句替换mysql_*函数,php,mysql,database,pdo,prepared-statement,Php,Mysql,Database,Pdo,Prepared Statement,我一直都在做简单的连接,mysql\u connect,mysql\u pcconnect: $db = mysql_pconnect('*host*', '*user*', '*pass*'); if (!$db) { echo("<strong>Error:</strong> Could not connect to the database!"); exit; } mysql_select_db('*database*'); 现在我知道这在某种
mysql\u connect
,mysql\u pcconnect
:
$db = mysql_pconnect('*host*', '*user*', '*pass*');
if (!$db) {
echo("<strong>Error:</strong> Could not connect to the database!");
exit;
}
mysql_select_db('*database*');
现在我知道这在某种程度上是安全的
它逃避危险人物;但是,它仍然容易受到其他攻击,这些攻击可能包含安全字符,但可能对显示数据或在某些情况下恶意修改或删除数据有害
所以,我搜索了一下,找到了关于PDO、MySQLi和prepared语句的信息。是的,我可能会迟到,但我已经阅读了很多很多教程(tizag、W3C、博客、谷歌搜索),没有一本提到这些。这似乎很奇怪,因为仅仅逃避用户输入是不安全的,至少可以说是不好的做法。是的,我知道你可以用正则表达式来解决这个问题,但我很确定这还不够
据我所知,当用户输入变量时,使用PDO/prepared语句是从数据库存储和检索数据的一种更安全的方法。唯一的问题是,转换(特别是在我以前的编码方式/习惯被卡住之后)有点困难
现在我明白了,要使用PDO连接到我的数据库,我将使用
$hostname = '*host*';
$username = '*user*';
$password = '*pass*';
$database = '*database*'
$dbh = new PDO("mysql:host=$hostname;dbname=$database", $username, $password);
if ($dbh) {
echo 'Connected to database';
} else {
echo 'Could not connect to database';
}
现在,函数名不同了,因此我的mysql\u查询
,mysql\u获取数组
,mysql\u num\u行
等不再工作。所以我不得不读/记一大堆新的,但这就是我感到困惑的地方
如果我想从(比如)注册/登记表中插入数据,我将如何进行,但主要是如何安全地进行?我假设这就是准备好的语句的用武之地,但是通过使用它们,是否就不需要使用类似于mysql\u real\u escape\u string的东西了?我知道mysql\u real\u escape\u string
要求您通过mysql\u connect
/mysql\u pcconnect
连接到数据库,所以现在我们也不使用它,这个函数会不会产生错误
我也见过不同的方法来处理PDO方法,例如,我见过:variable
和?
作为我认为的占位符(如果这是错误的,请抱歉)
但我认为这大概就是从数据库中获取用户应该做什么的想法
$user_id = $_GET['id']; // For example from a URL query string
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE `id` = :user_id");
$stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
但是我有两个问题,如果变量不是一个数字,而是一个文本字符串,如果我没有弄错的话,你必须在PDO:PARAM_STR
之后给出一个长度。但是,如果您不确定用户在putted数据中给出的值,那么如何给出一个设置的长度,它每次都会变化?无论哪种方式,据我所知,显示数据,然后你做
$stmt->execute();
$result = $stmt->fetchAll();
// Either
foreach($result as $row) {
echo $row['user_id'].'<br />';
echo $row['user_name'].'<br />';
echo $row['user_email'];
}
// Or
foreach($result as $row) {
$user_id = $row['user_id'];
$user_name = $row['user_name'];
$user_email = $row['user_email'];
}
echo("".$user_id."<br />".$user_name."<br />".$user_email."");
这样行吗?安全吗?如果它是正确的,我将为?\u LENGTH.
输入什么值?我完全搞错了吗
更新
到目前为止,我收到的回复非常有用,非常感谢你们!每个人都有一个+1,让我看到了一些不同的东西。很难选择最上面的答案,但我认为“榴霰弹上校”是值得的,因为所有的东西都几乎被覆盖了,甚至进入了其他具有自定义库的阵列,而我并不知道
但多亏了大家:)我从不为bindParam()或param类型或长度而烦恼
我只是将一组参数值传递给execute(),如下所示:
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE `id` = :user_id");
$stmt->execute( array(':user_id' => $user_id) );
$stmt = $dbh->prepare("INSERT INTO `users` (username, email)
VALUES (:username, :email)");
$stmt->execute( array(':username'=>$username, ':email'=>$email) );
这同样有效,而且更容易编码
您可能还对我的演示文稿或我的书感兴趣。要回答长度问题,指定它是可选的,除非您绑定的参数是存储过程中的OUT参数,因此在大多数情况下可以安全地忽略它
就安全而言,当绑定参数时,转义是在幕后完成的。这是可能的,因为在创建对象时必须创建数据库连接。您还可以免受SQL注入攻击,因为通过准备语句,您可以在用户输入接近语句之前告诉数据库语句的格式。例如:
$id = '1; MALICIOUS second STATEMENT';
mysql_query("SELECT * FROM `users` WHERE `id` = $id"); /* selects user with id 1
and the executes the
malicious second statement */
$stmt = $pdo->prepare("SELECT * FROM `users` WHERE `id` = ?") /* Tells DB to expect a
single statement with
a single parameter */
$stmt->execute(array($id)); /* selects user with id '1; MALICIOUS second
STATEMENT' i.e. returns empty set. */
因此,就安全性而言,你上面的例子似乎很好
最后,我同意单独绑定参数是一件乏味的事情,通过传递给PDOStatement->execute()的数组也可以有效地绑定参数(请参见)。感谢您提出的有趣问题。给你:
它避开了危险人物
你的概念完全是错误的。
其实“危险人物”是一个神话,根本没有。
和mysql_real_escape_字符串转义,但仅仅是一个字符串分隔符。从这个定义可以看出它的局限性——它只适用于字符串
但是,它仍然容易受到其他攻击,这些攻击可能包含安全字符,但可能对显示数据或在某些情况下恶意修改或删除数据有害
你把一切都混在这里。
说到数据库
- 对于字符串,不易受攻击。只要您的字符串被引用和转义,它们就不能“恶意修改或删除数据”。
*
- 对于其他数据类型data-是的,它是无用的。但这并不是因为它有点“不安全”,而是因为使用不当李>
至于显示数据,我想在PDO相关的问题中,它是offtopic,因为PDO也与显示数据无关。
转义用户输入
^^^另一个需要注意的错觉
- 用户输入与转义完全无关。从前面的定义中可以了解到,您必须转义字符串,而不是任何“用户输入”。因此,再一次:
- 您有转义字符串,无论其来源如何
- 逃逸其他类型的数据是没有用的,无论数据的来源是什么
明白了吗?
现在,
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE `id` = :user_id");
$stmt->execute( array(':user_id' => $user_id) );
$stmt = $dbh->prepare("INSERT INTO `users` (username, email)
VALUES (:username, :email)");
$stmt->execute( array(':username'=>$username, ':email'=>$email) );
$id = '1; MALICIOUS second STATEMENT';
mysql_query("SELECT * FROM `users` WHERE `id` = $id"); /* selects user with id 1
and the executes the
malicious second statement */
$stmt = $pdo->prepare("SELECT * FROM `users` WHERE `id` = ?") /* Tells DB to expect a
single statement with
a single parameter */
$stmt->execute(array($id)); /* selects user with id '1; MALICIOUS second
STATEMENT' i.e. returns empty set. */
$sql = 'SELECT * FROM `users` WHERE `name`=?s AND `type`=?s AND `active`=?i';
$data = $db->getRow($sql,$_GET['name'],'admin',1);
$sql = '
SELECT *
FROM `users`
WHERE
`name` LIKE :name
AND `type` = :type
AND `active` = :active
';
$stm = $db->prepare($sql);
$stm->bindValue(':name', $_GET['name']); // PDO::PARAM_STR is the default and can be omitted.
$stm->bindValue(':type', 'admin'); // This is not possible with bindParam().
$stm->bindValue(':active', 1, PDO::PARAM_INT);
$stm->execute();
...
$sql = 'SELECT * FROM `users` WHERE `id` = :id';
$stm = $db->prepare($sql);
$id = 0;
$stm->bindParam(':id', $id, PDO::PARAM_INT);
$userids = array(2, 7, 8, 9, 10);
foreach ($userids as $userid) {
$id = $userid;
$stm->execute();
...
}
try {
$db = new PDO(...);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING)
} catch (PDOException $e) {
echo 'Oops, something went wrong with the database connection.';
}