sql注入是因直接拼接用户输入导致数据库将恶意字符串误作可执行代码;参数化查询通过预编译固定sql结构、仅将输入作为数据传入,实现指令与数据的物理隔离,从而彻底阻断注入。

直接拼接用户输入会导致 SQL 注入,数据库会把恶意字符串当作可执行的 SQL 代码来解析和运行,而不是当作普通数据。
SQL 注入是怎么发生的
当用户输入被无差别地插入到 SQL 字符串中,比如:
username = $_GET['username']; $sql = "SELECT * FROM users WHERE username = '$username'";
攻击者传入 admin' OR '1'='1,最终执行的语句就变成:
SELECT * FROM users WHERE username = 'admin' OR '1'='1'
这会绕过所有条件,返回全表数据。更危险的是,攻击者还能注入 UNION SELECT、DROP TABLE 甚至系统命令(取决于数据库配置和权限)。
关键点在于:数据库无法区分“这段文字是查询逻辑”还是“这段文字是用户给的数据”。拼接操作彻底抹掉了这个边界。
为什么参数化查询能解决这个问题
参数化查询让数据库在预编译阶段就固定了 SQL 结构,所有用户输入只作为“值”传入,不参与语法解析。例如:
cursor.execute("SELECT * FROM users WHERE email = %s", (user_email,))
无论 user_email 是 test@example.com 还是 test' OR 1=1; DROP TABLE users-- ,它都只会被当作一个字符串值处理,不会改变 SQL 的执行逻辑。
- 数据库驱动自动处理转义,无需手动调用
mysql_real_escape_string等函数 - 即使输入含单引号、分号、注释符或二进制数据,也不会破坏语句结构
- MySQL 的
PREPARE+EXECUTE机制底层保障了指令与数据的物理隔离
LIKE 查询里拼接通配符也一样危险
很多人以为只在 WHERE a = ? 场景才需要防注入,但 LIKE 同样不能拼接:
$keyword = $_GET['q']; $sql = "SELECT * FROM products WHERE name LIKE '%$keyword%'"; // ❌ 危险!
攻击者传入 %' OR '1'='1,就能逃逸出 LIKE 上下文,触发布尔盲注或报错注入。
正确做法是把整个带通配符的值作为参数传递:
$stmt = $pdo->prepare("SELECT * FROM products WHERE name LIKE ?");
$full_keyword = '%' . $keyword . '%';
$stmt->execute([$full_keyword]);
注意:% 和 _ 是 LIKE 的通配符,如果业务允许用户输入这些字符并希望按字面匹配,还需额外调用 ESCAPE 子句或使用 addcslashes() 转义,但这属于语义需求,不影响注入防护的根本逻辑。
拼接表名或字段名是另一类错误
参数化查询无法绑定表名、字段名、排序方向(ASC/DESC)或 LIMIT 的偏移量。这类动态部分必须通过白名单校验来控制:
- 用
in_array($table_name, ['users', 'orders', 'products'])严格限定可选值 - 排序字段从固定映射数组中取:
$sort_map = ['name' => 'username', 'time' => 'created_at'] -
LIMIT的数字参数可用(int)强转或filter_var($n, FILTER_VALIDATE_INT)验证
任何试图用参数占位符代替标识符的操作(如 SELECT * FROM ?)都会直接报错 —— MySQL 不支持这种语法,强行绕过只会引入新的漏洞或运行时异常。
真正容易被忽略的是:哪怕只有一处拼接,整条链路的安全性就归零。不要依赖“用户没那么聪明”或“这只是内部系统”,自动化扫描工具几秒就能发现这类缺陷。











