动态查询便是指将变量放到语句中,将固定的字符串和变量拼接在一块儿,组成一个完整的SQL查询语句,因为变量的值是动态变化的,所以查询也是动态的。编写这样的可以执行的动态的查询语句是十分天然的,也很是方便。可是,不经思考的加入也会带来很大的安全隐患。例如这样的一个查询:php
SELECT × FROM Bugs WHERE bug_id = $bug_id";
若是这个时候$bug_id = "1234; DELETE FROM Bugs",Bugs表就跪了。所以SQL注入的危害至关大,不能将未经验证的输入做为代码执行。mysql
没有一种技术可以彻底的抵抗SQL注入,所以,须要将多种作法结合起来正则表达式
转移能防止一些意外的状况,例如这个查询:sql
SELECT * FROM Projects WHERE project_name = '$project_name'
若是$project_name = O'Hare(这是很正常的一种状况),因为名字里面有个引号,查询就变成:数据库
SELECT * FROM Projects WHERE project_name = 'O'Hare'
这个时候数据库就会由于多了个引号而出错,所以须要在名字里面的'前加反斜杠。数组
参数化查询是指在编写查询语句的时候,在须要参数的位置上使用参数占位符,而后查询的时候提供这一参数。以php为例:安全
<?php $sql = "SELECT * FROM Projects WHERE project_name = ?"; $stmt = mysqli_prepare($con, $sql); mysqli_stmt_bind_param($stmt, 's', $_POST['name']); mysqli_stmt_execute($stmt);
简单来讲,数据库接到一个指令,大体是这么作的:函数
编译SQL生成执行计划优化
选择执行计划code
执行
SQL注入不少状况是增长查询语句,增长查询语句就会形成查询的语义发生变化(就是说编译会生成不一样的东西)。若是使用参数化查询,在准备查询语句的时候数据库就去编译,而后提供参数就会重用这个编译结果,即便使用了注入变量,语义也不会发生变化(不会从新编译),同时,传参的时候,数据库也会将变量进行过滤,以达到防止注入的效果。
参数化查询也有作不到的东西:
1.多个值的列表不能做为单一参数
若是编写这么一个查询:
SELECT * FROM Bugs WHERE bug_id IN (?)
那么你传递参数的时候不能是“1234,3456,5678”这样的,这样会被认为是一个字符串
2.表名,列名,SQL关键字不能做为参数
应该将全部不合法的字符去掉,而不是找是否有些输入包含了危险的内容。
好比说若是是须要一个整数,就应该只是用变量里面的整数部分,(在Php里面有filter扩展),能够直接用类型转换函数,也能够用正则表达式匹配
通常都用参数化查询,可是有时候,参数化查询会致使数据库采用错误的优化方案(多是采用错误的索引)。好比说表里面有一列99%的都是TRUE,那么用因此很快就能查询到那1%的FALSE。这个时候将变量直接插进去是好的作法。但这个时候就要格外当心的引用字符串。在php里面可使用PDO::quote()
好比说,你想要让用户选择究竟是按什么关键字排序,是升序排序,仍是按降序排序,这个时候你从客户端接受两个参数
<?php $sort_standard = $_POST['order']; $direction = $_POST['direction']; $sql = "SELECT * FROM Bugs ORDER BY $sort_standard $direction"; mysqli_query($con, $sql);
可是这个并非一个安全的作法,由于用户能够传递任意值。可是参数化查询又不能是关键字,采用这样的作法:
先声明$sort_standard和$direction数组,好比这样
$sort_standard = array( "status" => "status", "date" => "date_report" ); $direction = array( "up" => "ASC", "down" => "DESC" );
当判断到用户的选择不在数组中的时候就使用默认值,若是在数组中就用对应值,这样作:
从不使用用户的输入进行查询,减小了注入风险
让语句动态化,而没有语法上的限制
将数据库查询和用户界面解藕