注入示例
# step 1 SQL
SELECT * FROM users WHERE username = 'admin' AND password = 'e10adc3949ba59abbe56e057f20f883e'
# step 2 SQL
SELECT * FROM users WHERE username = 'admin'#' AND password = '96e79218965eb72c92a549dd5a330112'
关键点是这2个SQL的区别.其中第二步由于前台传入特殊字符单引号(')及#,在数据库中#为语句注释部分,则后续SQL语句不会被执行,可直接跳过我们的密码验证逻辑。
username还可以是 'admin' OR 1='1 跳过验证
1. PHP端防注入
1.1 字符型过滤
/*discuz过滤函数*/
function inject_check($sql_str)
{
return preg_match('/^select|insert|and|or|create|update|delete|alter|count|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/i', $sql_str); // 进行过滤
}
$data[] = $_GET['username'];
$data[] = $_GET['passwd'];
function inject_checks($data) {
foreach($data as $key => $val){
if(preg_match('/^select|insert|and|or|create|update|delete|alter|count|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/i', $val)){
return true;
}
}
return false;
}
上面是discuz过滤字符型注入的函数 ,返回检测结果,1 or 0
1就是存在特殊字符
示例:
if(inject_check($str)){
exit('非法字符!');
}
foreach($_POST as $key => $value){
if(inject_check($value)){
echo '非法字符';
exit();
}
}
这种要求比较严格的过滤,一般应用在注册和登陆环节
1.2 数字型的过滤
$id = intval($_GET['id']);
数字型的就比较简单了。只需要转换一下
1.3 富文本的过滤
$html = "<a href='test'>Test</a>";
$html = addslashes($html);
输出:
<a href=\'test\'>Test</a>
需要显示的时候再用 stripslashes 处理
1.4 统一转义
if (!get_magic_quotes_gpc())
{
if (!empty($_GET))
{
$_GET = addslashes_deep($_GET);
}
if (!empty($_POST))
{
$_POST = addslashes_deep($_POST);
}
$_COOKIE = addslashes_deep($_COOKIE);
$_REQUEST = addslashes_deep($_REQUEST);
}
function addslashes_deep($value)
{
if (empty($value))
{
return $value;
}
else
{
return is_array($value) ? array_map('addslashes_deep', $value) : addslashes($value);
}
}
array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组。
这样数组中的每个值都得到转义处理
2. PDO方式防止注入
pdo中的预处理语句
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "haha";
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// 设置 PDO 错误模式为异常
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 预处理 SQL 并绑定参数
$stmt = $conn->prepare("select * from dw_user where `username`=:username and `password`=:password");
$stmt->bindParam(':username', $firstname);
$stmt->bindParam(':password', $pass);
// 插入行
$firstname = $_POST['username'];
$pass = $_POST['password'];
//$lastname = "Doe";
//$email = "john@example.com";
$stmt->execute();
$rs = $stmt->Fetch();
if($rs['user_id'] > 0 ){
echo '登陆成功!';
exit();
}else{
echo '登陆失败!';
exit();
}
// 插入其他行
//$firstname = "Mary";
//$lastname = "Moe";
//$email = "mary@example.com";
//$stmt->execute();
// 插入其他行
//$firstname = "Julie";
//$lastname = "Dooley";
//$email = "julie@example.com";
//$stmt->execute();
//echo "新记录插入成功";
}
catch(PDOException $e)
{
echo "Error: " . $e->getMessage();
}
$conn = null;
预处理绑定 会变成字符串包起来 不是 简单的 SQL 拼接