参数化查询(Parameterized Query 或 Parameterized Statement)是指在设计与数据库链接并访问数据时,在需要填入数值或数据的地方,使用参数(Parameter) 来给值,这个方法目前已被视为最有效可预防SQL注入攻击 (SQL Injection) 的攻击手法的防御方式。
有部份的开发人员可能会认为使用参数化查询,会让程序更不好维护,或者在实现部份功能上会非常不便[来源请求],然而,使用参数化查询造成的额外开发成本,通常都远低于因为SQL注入攻击漏洞被发现而遭受攻击,所造成的重大损失。
除了安全因素,相比起拼接字符串的 SQL 语句,参数化的查询往往有性能优势。因为参数化的查询能让不同的数据通过参数到达数据库,从而公用同一条 SQL 语句。大多数数据库会缓存解释 SQL 语句产生的字节码而省下重复解析的开销。如果采取拼接字符串的 SQL 语句,则会由于操作数据是 SQL 语句的一部分而非参数的一部分,而反复大量解释 SQL 语句产生不必要的开销。
MySQL 的参数格式是以 "?" 字符加上参数名称而成。
UPDATE myTable SET c1 = ?c1, c2 = ?c2, c3 = ?c3 WHERE c4 = ?c4
PDO[编辑]
PDO用于PHP之内。 在使用 PDO 驱动时,参数查询的使用方法一般为:
// 实例化数据抽象层对象$db=new PDO('pgsql:host=127.0.0.1;port=5432;dbname=testdb');
// 对 SQL 语句执行 prepare,得到 PDOStatement 对象
$stmt=$db->prepare('SELECT * FROM "myTable" WHERE "id" = :id AND "is_valid" = :is_valid');
// 绑定参数$stmt->bindValue(':id',$id);$stmt->bindValue(':is_valid',true);
// 查询$stmt->execute();// 获取数据foreach($stmtas$row){var_dump($row);}
对于 MySQL 的特定驱动,也可以这样使用:
$db=new mysqli("localhost","user","pass","database"); $stmt=$db->prepare("SELECT priv FROM testUsers WHERE username=? AND password=?"); $stmt->bind_param("ss",$user,$pass);$stmt->execute();
值得注意的是,以下方式虽然能有效防止 SQL注入 (归功于 mysql_real_escape_string 函数的转义),但并不是真正的参数化查询。其本质仍然是拼接字符串的 SQL 语句。
$query=sprintf("SELECT * FROM Users where UserName='%s' and Password='%s'", mysql_real_escape_string($Username),mysql_real_escape_string($Password));mysql_query($query);