重现一次 SQL 注入,用预处理语句将其化解,并应用最小权限原则。
在 Kodokon 中打开本课SQL 注入发生在不可信数据被拼接进查询文本的那一刻。攻击者提供一个会闭合字符串并劫持逻辑的值。让我们在一张 users 表上重现这次攻击:直接拼接输入 ada' OR '1'='1,会把一个精确的过滤变成一个恒真的条件。
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
is_admin INTEGER NOT NULL DEFAULT 0
);
INSERT INTO users (id, name, is_admin)
VALUES (1, 'ada', 0), (2, 'root', 1);
-- Dangerous concatenation (NEVER do this)
-- input = ada' OR '1'='1
SELECT * FROM users
WHERE name = 'ada' OR '1'='1';这个载荷之所以奏效,是因为引号过早地闭合了字面量 'ada',接着 OR '1'='1' 追加了一个普遍为真的条件。一个变体是在末尾附加 -- 来把原查询余下的部分注释掉。教训在于:只要数据和代码共用同一个字符串,任何手动转义都不是真正可靠的。
-- Positional placeholder
SELECT * FROM users WHERE name = ?;
-- Named placeholder
SELECT * FROM users WHERE name = :name;补救办法是预处理语句:驱动先编译查询的结构,然后单独绑定这些值。被绑定的数据永远不会被当作 SQL 来解析,只会被用于比较。作为额外的好处,编译好的结构是可复用的:接连绑定多个值,可以避免每次调用都重新编译计划。
-- The bound value stays data, not SQL
SELECT * FROM users
WHERE name = :name;
-- :name = ada' OR '1'='1
-- -> 0 rows: no user
-- is literally named that' OR '1'='1 之所以能注入成功,是因为它……? 参数来绑定表名吗?:name