看现象
创建一个测试数据库表,插入测试数据:
代码语言:javascript复制CREATE TABLE `blank_space` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uid` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`desc` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_key` (`uid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `blank_space`(`id`, `uid`, `desc`) VALUES (1, 'abc ', '末尾1个');
INSERT INTO `blank_space`(`id`, `uid`, `desc`) VALUES (2, ' abc', '开头1个');
INSERT INTO `blank_space`(`id`, `uid`, `desc`) VALUES (3, ' abc', '开头2个');
id | uid | desc |
---|---|---|
1 | abc_ | 末尾 1 个 |
2 | _abc | 开头 1 个 |
3 | __abc | 开头 2 个 |
uid 实际上没有
_
,这样写是为了看到空格。
执行操作:
代码语言:javascript复制SELECT * FROM blank_space WHERE uid = 'abc';
SELECT * FROM blank_space WHERE uid = 'abc ';
SELECT * FROM blank_space WHERE uid = 'abc ';
都可以查询出:
id | uid | desc |
---|---|---|
1 | abc_ | 末尾 1 个 |
执行操作:
代码语言:javascript复制INSERT INTO `blank_space`(`uid`, `desc`) VALUES ('abc', '无空格');
-- 1062 - Duplicate entry 'abc' for key 'uniq_key', Time: 0.322000s
INSERT INTO `blank_space`(`uid`, `desc`) VALUES ('abc ', '末位两个');
-- 1062 - Duplicate entry 'abc' for key 'uniq_key', Time: 0.322000s
原因
MySQL 校对规则属于 PADSPACE,会忽略尾部空格。针对的是 varchar char text 等文本类的数据类型。此为 SQL 标准化行为。无需要设置也无法改变。
解决方案
代码语言:javascript复制SELECT * FROM blank_space WHERE uid = BINARY 'abc';
-- 0 records
SELECT * FROM blank_space WHERE uid = BINARY 'abc ';
-- 1 records
SELECT * FROM blank_space WHERE uid = BINARY 'abc ';
-- 0 records
SELECT * FROM blank_space WHERE uid like 'abc';
-- 0 records
SELECT * FROM blank_space WHERE uid like 'abc ';
-- 1 records
SELECT * FROM blank_space WHERE uid like 'abc ';
-- 0 records
BINARY 不是函数,是类型转换运算符,它用来强制它后面的字符串为一个二进制字符串,可以理解成精确匹配。
约束攻击
需要在非严格模式下。
代码语言:javascript复制SET sql_mode = '';
CREATE TABLE `blank_space_attack` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uid` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`pwd` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `blank_space_attack`(`id`, `uid`, `pwd`) VALUES (1, 'admin', '123');
INSERT INTO `blank_space_attack`(`id`, `uid`, `pwd`) VALUES (2, 'tim', '234');
攻击:
代码语言:javascript复制INSERT INTO `blank_space_attack`(`uid`, `pwd`) VALUES ('admin 1', 'easy');
结果:
代码语言:javascript复制
select * from blank_space_attack where uid = "admin" and pwd = "easy";
id | uid | desc |
---|---|---|
3 | admin_____ | easy |
限制条件
- 服务端没有对用户名长度进行限制。如果服务端限制了用户名长度就不能导致数据库截断,也就没有利用条件。
- 登陆验证的 SQL 语句必须是用户名和密码一起验证。如果是验证流程是先根据用户名查找出对应的密码,然后再比对密码的话,那么也不能进行利用。因为当使用 admin 为用户名来查询密码的话,数据库此时就会返回两条记录,而一般取第一条则是目标用户的记录,那么你传输的密码肯定是和目标用户密码匹配不上的。
- 验证成功后返回的必须是用户传递进来的用户名,而不是从数据库取出的用户名。因为当我们以用户 admin 和密码 easy 登陆时,其实数据库返回的是我们自己的用户信息,而我们的用户名其实是
admin_____
,如果此后的业务逻辑以该用户名为准,那么就不能达到越权的目的了。
References
- SQL 约束攻击 | v0n
- 记一次数据库空格问题 | iluoy
- Mysql 查询条件中字符串尾部有空格也能匹配上的问题 | xjnotxj
– EOF –
- # mysql