SQL注入攻击的原理、检测与预防

SQL 注入是数据库应用最常见的被攻破方式:攻击者把带 SQL 语义的字符串塞进程序的输入口,让原本的数据查询变成任意语句执行。这里记录它的成因、检测思路和防御手段。

注入是怎么发生的

以登录校验为例。程序把用户名和密码直接拼进 SQL,再去 MySQL 的 users 表比对是否有对应行:

sql ="SELECT COUNT(*) FROM users WHERE user='"
+ username + "' AND pass = '" + password + "'";

输入 Tom 和 Welcome01 时,语句是正常的:

SELECT COUNT(*) FROM users WHERE user='Tom'
 AND pass = 'Welcome01';

但用户名换成 abc、密码换成 x’ OR 1=1 LIMIT 1;– 时,拼出来变成:

SELECT COUNT(*) FROM users WHERE user='x'
 AND pass = 'x' OR 1=1 LIMIT1;-- '

OR 1=1 恒真,尾部被 — 注释掉,攻击者拿着错误密码也能通过校验。同一手法还能非法创建用户、删库、篡改重要数据。所以任何用户输入都不可信:引号、转义符这类有 SQL 特殊含义的字符,必须在数据进库前被清除或做转义处理。即便是公开可查的数据也不安全——对它的恶意查询照样能拖垮服务器;受保护的数值和字符型数据更需要严格校验。

检测潜在的注入输入

常见的注入试探有三种:

  • 在网页表单里输入单引号或双引号
  • 修改动态 URL,追加 22%(”)、23%(#)、27%(’)
  • 在数值栏里输入字符、空格、特殊符号

应用把输入传给数据库之前,要对以上内容做剔除或报错。如果三种输入都能原样进入查询,说明应用缺少安全处理,需要开发侧修改;若这些字符本身是业务必需的,则要特殊处理、去掉其 SQL 含义。

预防原则:不拼接、用参数

  • 永远不要把用户提供的文本与 SQL 语句直接连接
  • 查询用到用户文本时,走带参数的存储过程或预处理语句:它们不做带参数的宏展开;数值参数不接受文本;文本参数按字符串比较,而不是按 SQL 语法解释

漏洞写法与修复写法

典型的问题代码——$_POST 取值直接拼进查询:

php
$username = $_POST['username'];
$password = $_POST('password');
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";

登录页同理:

php
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username='$username' AND password='$password'";
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) == 1) {
    // 登录成功
} else {
    // 登录失败
}

当攻击者在用户名字段输入:

bash
admin' OR '1'='1

实际执行的查询变成:

sql
SELECT * FROM users WHERE username='admin' OR '1'='1' AND password='password'

‘1’=’1′ 恒真,查询返回所有用户记录而不只是密码匹配的那条,身份验证被绕过,全部用户的敏感信息随之暴露。修复方式是参数化查询,把输入当数据而不是代码:

php
$stmt = $conn->prepare("SELECT * FROM users WHERE username=? AND password=?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 1) {
    // 登录成功
} else {
    // 登录失败
}

用户输入通过绑定传给占位符,SQL 结构在使用前已经定型,输入再怎么特殊也改变不了语句语义,注入自然不成立。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容