SQL注入的产生一般是由:开发人员在编写过程中未对用户输入参数做处理,直接拼接SQL,导致数据库被篡改。
下面的Demo演示的是“恶意用户利用漏洞删除mysql数据”
<?php //原有的sql模块,不安全的,抛弃 function sql($sql){ /* * 这是一个为了使用方便而编写的sql方法。 * 所有select开头的sql返回查询结果, * 所有非select开头的sql返回受影响的行数。 */ $ip="***"; $port=3306; $username="***"; $password="***"; $database="***"; $return = 0; //创建PDO对象 $dsn = "mysql:host=$ip;dbname=$database;port=$port;charset=utf8"; if(preg_match("/^[iIuUdDsS][nNpPeE][sSdDlL][eEaA][rRtTcC][tTeE]/is",$sql)){ try{ $options = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, //默认是PDO::ERRMODE_SILENT, 0, (忽略错误模式) PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 默认是PDO::FETCH_BOTH, 4 ); $pdo = new PDO($dsn, $username, $password, $options); if (preg_match("/^[sS]/is",$sql)) { //使用query $stmt = $pdo->query($sql); //返回一个PDOStatement对象 $return = $stmt->fetchAll(); //获取所有 //$return = $stmt->rowCount(); //记录数 }else{ $return = $pdo->exec($sql); //返回受影响的行数 //$return = $pdo->lastInsertId();//返回修改的ID } $pdo = null; }catch(Exception $e){ $return = $e->getMessage(); } } return $return; } /* * Sql注入Demo,这是存在风险的实力: */ $k = $_GET['k']; $v = $_GET['v']; $i = file_get_contents('php://input'); if( strlen($k)>0 && ((strlen($v)>0 && count($_GET)==2)||( strlen($i)>0 && count($_GET)==1) )){ $v = strlen($v) > 0 ? $v : $i; echo sql("insert into kv(k,v)values('$k','$v');"); //没有使用预编译 exit(); } ?>
恶意用户访问:
1 curl "***.com/1.php?k=sql注入" -d "test');delete from kv;"
结果:kv表数据被全部删除!
MySQL [***_db]> select * from kv; Empty set (0.04 sec) MySQL [***_db]>
解决方法:通过PHP prepare预编译sql
代码如下:
<?php //进行预编译的sql方法 function ssql($sql,$arr=[],$json=false){ $ip="***"; $port=3306; $username="***"; $password="***"; $database="***"; $dsn = "mysql:host=$ip;dbname=$database;port=$port;charset=utf8"; $return = 0; if(preg_match("/^[iIuUdDsS][nNpPeE][sSdDlL][eEaA][rRtTcC][tTeE]/is",$sql)){ try{ $options = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, //默认是PDO::ERRMODE_SILENT, 0, (忽略错误模式) PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 默认是PDO::FETCH_BOTH, 4 ); $pdo = new PDO($dsn, $username, $password, $options);//创建PDO对象 $stmt = $pdo->prepare($sql);//2)使用prepare预处理 $precute = $stmt->execute($arr); //执行一条预处理语句 .成功时返回 TRUE, 失败时返回 FALSE if($precute){ if( preg_match("/^[sS]/is",$sql) ) { $sqlData = $stmt->fetchAll(); }else{ $sqlData = $stmt->rowCount(); //成功数 } if($json){ $return = json_encode($sqlData,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);//JSON_PRETTY_PRINT| } } $pdo = null; }catch(Exception $e){ } } return $return; } /* * 这是被测代码,使用预编译的sql方法 */ $k = $_GET['k']; $v = $_GET['v']; $i = file_get_contents('php://input'); if( strlen($k)>0 && ((strlen($v)>0 && count($_GET)==2)||( strlen($i)>0 && count($_GET)==1) )){ $v = strlen($v) > 0 ? $v : $i; echo ssql("insert into kv(k,v)values(?,?);",[$k,$v]); exit(); } ?>
恶意用户访问:
1 curl "***.com/1.php?k=sql注入" -d "test');delete from kv;"
结果:直接存入data,没有数据被删除。
MySQL [***_db]> select * from kv;
+----+------+-----------+------------------------+------+
| i | s | k | v | n |
+----+------+-----------+------------------------+------+
| 89 | NULL | sql注入 | test');delete from kv; | NULL |
+----+------+-----------+------------------------+------+
1 row in set (0.04 sec)
MySQL [***_db]>