PHP防止sql语句注入终极解决方案

完美解决方案就是使用拥有Prepared Statement机制(预处理sql)的PDO

//先做个实验 先不用预处理sql写法

<pre>

<?php

$pdo = new PDO('mysql:dbname=testdatabase;host=localhost;charset=utf8', 'root', 'root');

$id='2 or 1=1';

$stmt=$pdo->query('SELECT * FROM wz_admin WHERE id = '.$id);

print_r($stmt -> fetchAll ());

exit();

可以发现 可以输出数据 id=2的时候是没数据的

?>

</pre>

用预处理sql写法

$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

它会告诉 PDO 禁用模拟预处理语句,并使用 real parepared statements 。这可以确保SQL语句和相应的值在传递到mysql服务器之前是不会被PHP解析的(禁止了所有可能的恶意SQL注入攻击)。

<pre>

$pdo = new PDO('mysql:dbname=testdatabase;host=localhost;charset=utf8', 'root', 'root');

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$stmt = $pdo->prepare('SELECT * FROM wz_admin WHERE id = :id');

$id='2 or 1=1';

$stmt->execute(array('id' => $id));

print_r($stmt -> fetchAll ());

exit();

</pre>

可以发现没有输出数据

 

 

 

<pre>

上面这段代码就可以防范sql注入。为什么呢?

当调用 prepare() 时,查询语句已经发送给了数据库服务器,此时只有占位符 ? 发送过去,没有用户提交的数据;当调用到 execute()时,用户提交过来的值才会传送给数据库,它们是分开传送的,两者独立的,SQL攻击者没有一点机会

<?php

下面我简单的封装了下

class Db

{

private static $pdo;

public static function getPdo ()

{

if ( self::$pdo == null )

{

$host = 'localhost';

$user = 'root';

$pwd = '';

$dbname = 'testdatebase';

$dsn = "mysql:host=$host;dbname=$dbname;port=3306";

$pdo = new PDO ( $dsn, $user, $pwd );

$pdo->query('set names utf8;');

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

self::$pdo = $pdo;

}

return self::$pdo;

}

public static function getStmt($sql)

{

$pdo = self::getPdo();

return $pdo->prepare($sql);

}

public static function getinsertids()

{

$pdo = self::getPdo();

$insertid = $pdo->lastInsertId();

return $insertid;

}

public static function SETATTR_AUTOCOMMIT()

{

$pdo = self::getPdo();

$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);

}

public static function beginTransactions()

{

$pdo = self::getPdo();

$pdo->beginTransaction();//开启事务处理

}

public static function commits()

{

$pdo = self::getPdo();

$pdo->commit();//开启事务处理

}

public static function roolbacks()

{

$pdo = self::getPdo();

$pdo->rollBack();//开启事务处理

}

}

$sql = "SELECT * FROM wz_admin WHERE id = ?";

$stmt = Db::getStmt ( $sql );

$stmt -> execute ( array (1));

//PDO::FETCH_ASSOC这个参数代表只显示关联数组 默认是显示索引下标和关联数组的

print_r($stmt -> fetchAll (PDO::FETCH_ASSOC));

exit();

//以下返回1 都是数据库操作成功的

$sql = "INSERT INTO testss (wef,wef1) VALUES(?,?)";

$stmt = Db::getStmt ( $sql );

$dd=$stmt -> execute ( array ('wefe','wefe1'));

print_r($dd);

exit();

$sql = "update testss set wef=? where id=2";

$stmt = Db::getStmt ( $sql );

$dd=$stmt -> execute ( array ('34'));

print_r($dd);

 

 

//事务 只有commit得而时候才会执行sql语句 如果过程中报错就会roolbacks 返回到初始状态

/*DB::SETATTR_AUTOCOMMIT();

$username=$_POST['username'];

$sql = 'INSERT INTO arjianghu_register (username) VALUES(?)';

$stmt = \Db::getStmt($sql);

Db::beginTransactions();//开启事务处理

$isOk = $stmt->execute(array($username));

if(!$isOk){

Db::roolbacks();

echo json_encode(array('success'=>0,'msg'=>'网络繁忙','data'=>''));

exit();

}

$sql = 'INSERT INTO arjianghu_zhuangtai (personid) VALUES(?)';

$stmt = \Db::getStmt($sql);

$isOk = $stmt->execute(array(1));

if($isOk){

echo json_encode(array('success'=>1,'msg'=>'操作成功','data'=>''));

exit();

}else{

Db::roolbacks();

echo json_encode(array('success'=>0,'msg'=>'网络繁忙','data'=>''));

exit();

}

DB::commits();*/

?>

</pre>

ps:之所以预处理sql 能够防止注入式因为 他内部经过了转义等其他处理

ps:pdo字段不能用?必须要变量 ?只能用在值