%和_是sql通配符而非普通字符,直接拼接用户输入会导致语义劫持(如'100%'被当作模式)和sql注入(如'or 1=1 --'),必须通过参数化查询由程序添加通配符并预编译处理。

LIKE 查询里 % 和 _ 为什么不能直接拼进 SQL 字符串
因为 % 和 _ 是数据库原生通配符,不是普通字符。如果你把用户输入的关键词(比如用户搜 100%)直接用 + 拼进 SQL,像 "WHERE desc LIKE '%" + user_input + "%'",那 % 就会被当通配符展开,查出一堆不相关的记录;更糟的是,如果用户输 admin' OR 1=1 --,就触发 SQL 注入。
MySQL 中 escape 关键字怎么用才安全
MySQL 支持 ESCAPE 子句来指定转义字符,但必须和参数化查询配合——不能靠它单独防注入。比如你想查真实含 % 的字符串 discount: 50%,得先选一个转义符(常用 ),再在 SQL 里写:
SELECT * FROM products WHERE name LIKE '%50%%' ESCAPE ''
但注意:这个 必须由程序控制,不能让用户决定;否则攻击者传 \' 就可能绕过。所以实际使用中,ESCAPE 只用于明确需要匹配通配符本身的场景,且只在参数化查询内部使用。
常见错误包括:
- 在字符串拼接中硬编码
ESCAPE,导致转义符被用户污染 - 用
ESCAPE '/'却忘了把用户输入里的/全部双写,结果/被误识别为转义开始 - 在 PostgreSQL 或 SQLite 里照搬 MySQL 的
ESCAPE写法,语法报错
Python 里用 pymysql 或 mysql-connector-python 做参数化 LIKE 查询
核心原则:通配符必须由 Python 程序加,不能交给用户输入。用户只提供原始关键词,你来包裹 % 并传给占位符。
正确写法示例:
keyword = request.args.get("q", "") # 用户输入 "test"
search_pattern = f"%{keyword}%" # 在 Python 层加通配符
cursor.execute("SELECT * FROM items WHERE title LIKE %s", (search_pattern,))
这样 % 是代码写的,不是用户控制的;数据库收到的只是带通配符的完整字符串值,不会二次解析。
如果用户确实要搜索含 % 的字面量(比如查 “50% off” 这个短语),那就得额外处理:
- 先统一把用户输入中的
%替换成%,_替换成_ - SQL 里显式声明
ESCAPE '' - 确保所有替换都在参数化之前完成,且不依赖用户指定转义符
ORM 场景下 like() 方法为什么比手写 SQL 更可靠
像 SQLAlchemy 的 User.name.like(f"%{keyword}%") 不是字符串拼接,而是构造表达式树,最终生成的 SQL 会自动绑定参数、处理引号和转义。它底层仍走参数化协议,不会把 keyword 当 SQL 片段执行。
但要注意两个坑:
- 别在
like()外层再用str.format()或f""拼接字段名,比如f"{field}.like(...)"—— 字段名不属于参数范围,必须白名单校验 - 某些 ORM(如旧版 Flask-SQLAlchemy)对
ESCAPE支持有限,需手动调text(),这时必须用bindparam()注入所有变量,不能用f""
最易被忽略的一点:转义只解决通配符语义冲突,不替代参数化。哪怕你用了 ESCAPE,只要没走参数化,还是会被注入。两者是不同层级的防护,缺一不可。











