了解准备好的语句 - PHP

Posted

技术标签:

【中文标题】了解准备好的语句 - PHP【英文标题】:Understanding Prepared Statements - PHP 【发布时间】:2012-12-09 06:47:39 【问题描述】:

我正在为一所大学的财政援助办公室开展一个学校项目。该项目正在生产中,除了这里和那里的一些小调整外,大部分工作已经完成。我对寒假(现在)的主要关注是安全和尽我所能防止任何违规行为。人们告诉我要转向准备好的陈述。除了插入数据外,我在很大程度上理解它们。

我有两种表单:登录表单和学生登录表单。学生登录表单输入学生来办公室的原因。然后提交该表格,然后通过一个表格检索该数据,该表格向辅导员显示学生正在等待看到的内容。

我的问题是,虽然每个走进经济援助办公室的学生都有自己独特的问题(大部分时间),所以现在让我感到困惑的是:

我是否需要提前考虑并预先进行插入查询,或者是否有办法进行“动态”查询,因为有一个学生 cmets 框,因此它将是完全独一无二的,所以我将如何能够为此创建查询吗?

<?php
define('DB_Name', 'dbtest');
define('DB_User', 'root');
define('DB_Password', 'testdbpass');
define('DB_Host', 'localhost');

$link = mysql_connect(DB_Host, DB_User, DB_Password);

if (!$link) 
  die ('Could Not Connect: ' . mysql_error ());


$db_selected = mysql_select_db(DB_Name, $link);

if (!db_selected) 
  die('Can Not Use ' . DB_name . ': ' . mysql_error());


$value1 = $_POST ['anum'];
$value2 = $_POST ['first'];
$value3 = $_POST ['last'];
$value4 = $_POST ['why'];
$value5 = $_POST ['comments'];

$sql = "INSERT INTO `dbfinaid` (anum, first, last, why, comments) VALUES ('$value1', '$value2', '$value3', '$value4', '$value5')";

if (!mysql_query($sql)) 
  die('Error : ' . mysql_error());


mysql_close();

据我所知,这样做会使我容易受到 SQL 注入的影响。

任何帮助将不胜感激。谢谢你。

【问题讨论】:

显然准备好的语句可以是动态的。你刚刚读到这个:wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers 就是这样 pastebin 似乎没有加载。你能在你的问题中加入相关的代码吗? @MikeBrant:为我加载。但是,它是一种经典的 mysql_query() 方法,容易发生 SQL 注入 :) 我的建议是将您的mysql_* 操作重新设计为mysqli_* 操作,因为您真的不应该使用mysql_。特别是如果这是一个课堂项目,您不想向您的教授展示您如何无法阅读 PHP.net 上关于这些功能被弃用的大红色警告。如果您没有正确使用 MySQLi php 客户端,它们可能与 MySQL PHP 客户端一样容易被注入,所以不要认为只是从一个更改为另一个是灵丹妙药。这是使用准备好的语句的起点:php.net/manual/en/mysqli.prepare.php 如果你决定重写你的代码,比如说mysqli,我强烈建议你改用PDO。事实上,PDO 仍然有一些怪癖,但它远远优于 mysqli,尤其是在处理准备好的语句等内容时。 【参考方案1】:

基于@maček 的答案,这是做同样事情的另一种方法。我觉得这更容易:

$dbh = new PDO('mysql:host=localhost;dbname=dbtest', $user, $pass);

try 
  $query = $dbh->prepare("INSERT INTO `dbfinaid` (anum, first, last, why, comments)
    VALUES (:anum, :first, :last, :why, :comments)");

  $params = array_intersect_key($_POST, array_flip(array('anum', 'first', 'last', 'why', 'comments')));
  $query->execute($params);

catch (PDOException $e) 
  error_log($e->getMessage());
  die("An error occurred, contact the site administrator.");

我更喜欢将 SQL 错误输出到日志中,并向用户显示一个不会将它们与代码细节混淆的不同错误。

【讨论】:

所以 macek 和您的代码将允许每个学生的“cmets”框是唯一的,它将防止诸如 SQL 注入之类的攻击?我已经在教程中看到了这两个示例,我只是不明白按照你们俩的规定进行操作是否可以防止对我的数据库的攻击。我读对了吗? @BillKarwin,我不得不说我很荣幸你能扩展我的一篇文章。您如何确保参数与正确的类型绑定? @maček,MySQL 不关心绑定参数的 PHP 类型,因此您为 PDO 参数类型指定什么并不重要。 mysqldnd 驱动程序处理 LONGBLOB 与标量类型不同,但 PDO 无论如何都不支持 LONGBLOB。 @BillKarwin,我正在重新审视这个。你需要在你的白名单参数数组上调用array_flip。否则你会得到一个空的结果。【参考方案2】:

一旦你阅读了PHP's PDO,你就可以像这样重写你的代码

$dbh = new PDO('mysql:host=localhost;dbname=dbtest', $user, $pass);

try 
  $query = $dbh->prepare("INSERT INTO `dbfinaid` (anum, first, last, why, comments) VALUES (:anum, :first, :last, :why, :comments)");

  $query->bindParam(':anum',     $_POST['anum'],     PDO::PARAM_INT);
  $query->bindParam(':first',    $_POST['first'],    PDO::PARAM_STR);
  $query->bindParam(':last',     $_POST['last'],     PDO::PARAM_STR);
  $query->bindParam(':why',      $_POST['why'],      PDO::PARAM_STR);
  $query->bindParam(':comments', $_POST['comments'], PDO::PARAM_STR);

  $query->execute();

catch (PDOException $e) 
  die("error occured:" . $e->getMessage());

【讨论】:

以上是关于了解准备好的语句 - PHP的主要内容,如果未能解决你的问题,请参考以下文章

了解 PDO 准备好的语句和绑定参数

如何将 MySQLi 准备好的语句与存储过程一起使用

在 PHP 中创建准备好的语句

在 PHP 中到处使用准备好的语句? (PDO)

PHP:使用准备好的语句并防止 SQL 注入与逃逸

使用 PHP 和 MSSQL 准备好的语句