我有下表,我试图将“名称”列限制为范围从 0x20 到 0x7E 的 ASCII 字符(所有可打印的 ASCII 字符)。为什么我的检查约束不起作用?
CREATE TABLE test
(
id INTEGER NOT NULL,
name TEXT NOT NULL CHECK(name NOT GLOB '*[^\x20-\x7E]*'),
PRIMARY KEY(id)
)
例如:
INSERT INTO test (name) VALUES("Tom");
预期:应将名称为“Tom”的元组添加到表中(因为插入的名称仅包含可打印的 ASCII 字符)。结果:CHECK 约束失败:测试
反斜杠在 SQL 字符串中并不特殊。例如,'\x20' 是 4 个单独的字符,而不是转义的单个字符。所以你的
GLOB
模式总是匹配(因为 T 不是集合中的字符),并且 NOT
匹配,因此约束失败。
name NOT GLOB cast(x'2a5b5e202d7f5d2a' as TEXT)
为了确保字符串中的所有字符都是可打印的 ASCII,我们尝试查找不是的字符。如果没有找到这样的字符,则该字符串是可打印的 ASCII。从概念上讲,查询可能如下所示:
name NOT GLOB '*[^\x20-\x7E]*' -- (doesn't work in SQLite)
这里
GLOB
运算符匹配任何不在 0x20-0x7E
中的字符,并且由于我们用 NOT
否定表达式,如果没有找到范围之外的字符,查询将解析为逻辑 true
。但是,SQLite 不允许您转义字符串文字中的十六进制字符,例如,'\x20'
不是十六进制编码的空格字符。作为解决方法,我们提供与字节序列 GLOB
相同的 x'2a5b5e202d7f5d2a'
模式,并将其转换为 TEXT
,留下:
name NOT GLOB cast(x'2a5b5e202d7f5d2a' as TEXT)
对于合法的开发人员来说,这可能太丑陋了,但对于我们这些顽皮的搞 SQL 注入的流氓来说,这简直是完美的。
PS:我在 SQL 盲注框架中实现了类似的逻辑。有兴趣的读者可以在这里找到它。