如何在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一些好的提示:
- http://webmaster-forums.code-head.com/showthread.php?t=939
- http://www.sitepoint.com/article/php-security-blunders/
- http://dev.mysql.com/tech-resources/articles/guide-to-php-security.html
- http://www.scribd.com/doc/17638718/Module-11-PHP-MySQL-Database-Security-16
我认为最重要的两个项目是:
- 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接口上添加一行或两行