MySQL 5.6如何限制SELECT子句计算内容以防范SQL注入?
限制MySQL SELECT子句动态表达式的注入风险
针对你遇到的这个动态表达式注入问题,这里有几个靠谱的解决方案,按安全性优先级排序:
1. 严格的白名单验证(最推荐)
这是防御注入最安全的方式,只允许用户输入预先定义的合法元素,完全拒绝任何超出范围的内容。具体可以这么做:
- 定义允许的元素集合:
- 合法字段名:只能是字母、数字、下划线组成的业务表字段(比如
a、b) - 合法运算符:仅支持算术运算的
+、-、*、/、% - 合法聚合函数:仅允许业务需要的,比如
SUM()、AVG()、COUNT()等,且函数参数只能是合法字段 - 辅助语法:
AS、合法别名(字母、数字、下划线)、括号、逗号
- 合法字段名:只能是字母、数字、下划线组成的业务表字段(比如
- 用正则表达式做初步校验,比如这个示例正则(可根据你的业务调整):
这个正则会:^(?!.*@@)([a-zA-Z0-9_]+(\s*[+\-*/%]\s*[a-zA-Z0-9_]+)*|\s*(SUM|AVG|COUNT|MAX|MIN)\s*\(\s*[a-zA-Z0-9_]+\s*\))(\s*AS\s*[a-zA-Z0-9_]+)?(\s*,\s*([a-zA-Z0-9_]+(\s*[+\-*/%]\s*[a-zA-Z0-9_]+)*|\s*(SUM|AVG|COUNT|MAX|MIN)\s*\(\s*[a-zA-Z0-9_]+\s*\))(\s*AS\s*[a-zA-Z0-9_]+)?)*$- 直接拦截任何
@@开头的系统变量(比如@@version) - 只允许合法的算术表达式和指定聚合函数
- 确保别名格式合规
- 直接拦截任何
- 注意:正则只是初步校验,复杂场景下建议用词法解析工具(比如ANTLR生成MySQL语法解析器),把用户输入拆成token后逐个验证,彻底排除非法语法。
2. 解析表达式并过滤危险内容
如果白名单太严格,需要支持稍复杂的表达式,可以先对用户输入的表达式做语法解析:
- 拆分表达式的各个组成部分(变量、运算符、函数等)
- 逐一检查每个部分:
- 禁止所有以
@@开头的系统变量 - 禁止调用
LOAD_FILE()、EXECUTE()这类危险函数 - 禁止子查询、UNION等跨查询语法
- 只允许引用当前业务表的字段,不能访问其他表或系统库内容
- 禁止所有以
- 简单的解析逻辑示例(伪代码):
def validate_expression(expr): tokens = tokenize(expr) for token in tokens: if token.startswith('@@'): raise ValueError("禁止使用系统变量") if token in ['LOAD_FILE', 'EXECUTE']: raise ValueError("禁止调用危险函数") # 其他自定义验证规则... return True
3. 限制数据库用户权限(辅助防御)
即使前面的验证出现漏洞,也要尽量降低攻击危害:
- 创建专用的应用数据库用户,只赋予当前业务表的SELECT权限,禁止访问
information_schema、mysql等系统库 - 禁止用户执行
SHOW VARIABLES、SHOW TABLES等敏感命令 - 示例权限设置SQL:
这样即使注入了CREATE USER 'app_user'@'%' IDENTIFIED BY 'your_strong_password'; GRANT SELECT ON your_database.your_table TO 'app_user'@'%'; REVOKE ALL PRIVILEGES ON *.* FROM 'app_user'@'%'; FLUSH PRIVILEGES;@@version,攻击者也无法获取更多敏感库表信息。
4. 绝对禁止直接拼接未验证的用户输入
永远不要把用户输入的表达式直接拼接到SQL语句里!正确的流程是:
- 先通过前面的验证逻辑确认表达式合法
- 再将验证后的表达式拼接到SQL中(因为已经确保合法,所以安全)
- 错误示例(严禁这么做):
# 危险:直接拼接未验证的用户输入 sql = f"SELECT {user_input_expr} FROM table_name;" - 正确流程:
if validate_expression(user_input_expr): sql = f"SELECT {user_input_expr} FROM table_name;" # 执行SQL逻辑 else: # 返回非法输入的错误提示
内容的提问来源于stack exchange,提问作者Sid
相关产品推荐
相关产品推荐

