防sql注入需要结合三方面来做:
参数检查,包括参数合法性和长度校验,对于特殊字符,还需要做特殊字符处理
数据库设计使用正确的数据类型,如果是字符类型,设定字符的长度
使用预定义语句和参数化绑定数据
php提供了MySQLi和PDO两种选择。
MySQLi:
$mysqli = new mysqli('localhost', 'user', 'password', 'user');
$stmt = $mysqli->prepare("INSERT INTO user (account,nickname,tall) VALUES (?, ?, ?)");
$account = 'jack';
$nickname = 'jack';
$tall = 18.5;
$stmt->bind_param('ssd', $account, $nickname, $tall);
PDO:
$account = 'jack';
$sql = 'SELECT account, nickname, tall FROM user WHERE account = :account';
$pdo = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$sth = $pdo->prepare($sql);
$sth->execute(array(':account' => $account));
$results = $sth->fetchAll();
PDO默认情况下是使用模拟预定义语句。PDO模拟预定义语句是PDO在模拟服务端对参数和sql语句进行绑定,这个过程包括对字符串进行转义。就类似于自己用mysqlrealescape_string做字符串转义后再拼接字符串,然后在发送到数据库服务器。所以需要禁用模拟预定义
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
但是有些情况下是避免不了拼接sql的。对于动态SQL,即sql查询的表、字段或者排序的字段、方向不确定,是变量,这时需要外部传参进来拼接成sql,如
$sql = 'select * from user order by '. $orderfield . $direction;
这种情况就需要传入的参数做检查,建立白名单
如
$orders = array("account","tall","nickname"); //允许排序的字段
$key = array_search($_GET['sort'],$orders));
$orderfield = $orders[$key];