如何在PHP中创build一个安全的MySQL准备语句?

我是新来使用MySQL中准备好的语句与PHP。 我需要一些帮助创build一个准备好的语句来检索列。

我需要从不同的栏目获取信息。 目前对于testing文件,我使用完全不安全的 SQL语句:

$qry = "SELECT * FROM mytable where userid='{$_GET['userid']}' AND category='{$_GET['category']}'ORDER BY id DESC" $result = mysql_query($qry) or die(mysql_error()); 

有人可以帮助我创build一个安全的 mysql语句使用来自url参数的input(如上),准备好了吗?

奖金:准备好的表述也是为了提高速度。 如果我只在页面上使用三四次准备好的语句,它会提高整体速度吗?

下面是一个使用mysqli的例子(对象语法 – 如果你愿意,很容易翻译成函数的语法):

 $db = new mysqli("host","user","pw","database"); $stmt = $db->prepare("SELECT * FROM mytable where userid=? AND category=? ORDER BY id DESC"); $stmt->bind_param('ii', intval($_GET['userid']), intval($_GET['category'])); $stmt->execute(); $stmt->store_result(); $stmt->bind_result($column1, $column2, $column3); while($stmt->fetch()) { echo "col1=$column1, col2=$column2, col3=$column3 \n"; } $stmt->close(); 

另外,如果您想要一个简单的方法来抓取关联数组(而不是必须指定要绑定的variables),那么这里有一个方便的函数:

 function stmt_bind_assoc (&$stmt, &$out) { $data = mysqli_stmt_result_metadata($stmt); $fields = array(); $out = array(); $fields[0] = $stmt; $count = 1; while($field = mysqli_fetch_field($data)) { $fields[$count] = &$out[$field->name]; $count++; } call_user_func_array(mysqli_stmt_bind_result, $fields); } 

要使用它,只需调用它而不是调用bind_result:

 $stmt->store_result(); $resultrow = array(); stmt_bind_assoc($stmt, $resultrow); while($stmt->fetch()) { print_r($resultrow); } 

你可以这样写:

 $qry = "SELECT * FROM mytable where userid='"; $qry.= mysql_real_escape_string($_GET['userid'])."' AND category='"; $qry.= mysql_real_escape_string($_GET['category'])."' ORDER BY id DESC"; 

但是要使用准备好的语句,最好使用通用库,如PDO

 <?php /* Execute a prepared statement by passing an array of values */ $sth = $dbh->prepare('SELECT * FROM mytable where userid=? and category=? order by id DESC'); $sth->execute(array($_GET['userid'],$_GET['category'])); //Consider a while and $sth->fetch() to fetch rows one by one $allRows = $sth->fetchAll(); ?> 

或者,使用mysqli

 <?php $link = mysqli_connect("localhost", "my_user", "my_password", "world"); /* check connection */ if (mysqli_connect_errno()) { printf("Connect failed: %s\n", mysqli_connect_error()); exit(); } $category = $_GET['category']; $userid = $_GET['userid']; /* create a prepared statement */ if ($stmt = mysqli_prepare($link, 'SELECT col1, col2 FROM mytable where userid=? and category=? order by id DESC')) { /* bind parameters for markers */ /* Assumes userid is integer and category is string */ mysqli_stmt_bind_param($stmt, "is", $userid, $category); /* execute query */ mysqli_stmt_execute($stmt); /* bind result variables */ mysqli_stmt_bind_result($stmt, $col1, $col2); /* fetch value */ mysqli_stmt_fetch($stmt); /* Alternative, use a while: while (mysqli_stmt_fetch($stmt)) { // use $col1 and $col2 } */ /* use $col1 and $col2 */ echo "COL1: $col1 COL2: $col2\n"; /* close statement */ mysqli_stmt_close($stmt); } /* close connection */ mysqli_close($link); ?> 

我同意其他几个答案:

  • PHP的ext/mysql不支持参数化的SQL语句。
  • 在防止SQL注入问题方面,查询参数被认为更可靠。
  • 如果使用正确, mysql_real_escape_string()也可以有效,但是代码更加冗长。
  • 在某些版本中,国际字符集中的字符不能正确转义,只留下微妙的漏洞。 使用查询参数可以避免这些情况。

您还应该注意,即使使用查询参数,您仍然必须谨慎对待SQL注入,因为参数只取代SQL查询中的字面值。 如果dynamic构buildSQL查询并将PHPvariables用于表名,列名或SQL语法的任何其他部分,则在这种情况下查询参数或mysql_real_escape_string()mysql_real_escape_string() 。 例如:

 $query = "SELECT * FROM $the_table ORDER BY $some_column"; 

关于performance:

  • 当您使用不同的参数值多次执行准备好的查询时,会带来性能上的好处。 避免了parsing和准备查询的开销。 但是多长时间需要在同一个PHP请求中多次执行相同的SQL查询呢?
  • 即使您可以利用这种性能优势,与其他解决性能问题的其他方法相比,通常也只是略有改进,例如有效使用操作码caching或数据caching。
  • 甚至有些情况下,准备好的查询会损害性能。 例如,在以下情况下,优化程序不能假定它可以使用索引进行search,因为它必须假定参数值可能以通配符开头:

     SELECT * FROM mytable WHERE textfield LIKE ? 

PHP中的MySQL安全(或者其他语言)是一个很大的讨论问题。 这里有几个地方让你select一些好的提示:

我认为最重要的两个项目是:

  • SQL注入:确保使用PHP的mysql_real_escape_string()函数(或类似的东西)来转义所有的查询variables。
  • inputvalidation:永远不要相信用户的input。 看到这个教程如何正确消毒和validation您的input。

编写的语句不被普通的旧的Mysql / PHP接口支持。 你需要PDO或者mysqli 。 但是,如果你只是想replace占位符, 检查这个评论在php的mysql_query手册页。

如果你打算使用mysqli – 这对我来说似乎是最好的解决scheme – 我强烈推荐下载一个codesense_mysqli类的副本。

这是一个整洁的小类,它包装和隐藏了使用原始mysqli时积累的大部分信息,这样使用预处理语句只需要在旧的mysql / php接口上添加一行或两行