QSqlTableModel执行参数化查询防范SQL注入的问题排查
QSqlTableModel实现LIKE模糊查询并防范SQL注入的正确方案
现有方案问题分析
- 尝试1(字符串拼接SQL):直接将用户输入通过字符串拼接嵌入SQL语句,没有任何转义逻辑,属于典型的SQL注入漏洞写法,用户输入的单引号、UNION语句等可以直接篡改SQL执行逻辑,必须完全废弃。
- 尝试2(setFilter拼接字符串):两个核心错误:① 手动调用
setQuery()传入自定义查询后,setFilter()方法完全不生效——setFilter()仅在模型自动生成查询语句(未手动传入自定义query、通过setTable()指定表名)时才会在select()阶段拼接过滤条件;② 传入setFilter()的内容依然是字符串拼接用户输入,本身仍存在SQL注入风险。 - 尝试3(参数绑定但执行逻辑错误):参数绑定的思路是正确的,但存在两个流程错误:① 完成
prepare()和bindValue()后没有调用q.exec()执行查询,传入setQuery()的是未执行的空查询对象;② 传入自定义query后又调用model->select(),会触发模型自动生成默认查询覆盖你手动设置的结果集,最终导致无数据返回。
正确实现代码
方案1(推荐,官方原生写法,无注入风险)
优先使用QSqlTableModel原生的表映射能力,不需要手动编写全量SELECT语句,通过官方提供的参数绑定接口传值,从根源避免注入,同时保留模型所有内置的增删改、排序、分页能力(Qt 5.14及以上版本支持):
// 初始化模型时传入数据库连接对象,避免使用默认连接的坑 SqlTableModel *model = new SqlTableModel(this, db); model->setTable("table"); // 直接指定查询的表名,模型自动生成合规SQL // 过滤条件用?做占位符,绝对不要拼接用户输入 model->setFilter("col LIKE ? ESCAPE '\\\\'"); // 绑定参数:先转义用户输入中自带的LIKE通配符,再拼接查询用的%通配符 QString keyword = edit->text().trimmed(); keyword.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_"); model->setQueryParameters(QSql(), {QString("%%1%").arg(keyword)}); // 执行查询,必须加错误判断 if (!model->select()) { qDebug() << "查询失败:" << model->lastError().text(); return; } tableView->setModel(model);
方案2(兼容低版本Qt,自定义参数化查询)
如果使用Qt 5.14以下版本,可手动编写参数化查询,严格按照prepare->bindValue->exec的流程执行成功后再传入模型:
QSqlQuery q(db); // 编写参数化SQL,禁止拼接任何用户输入内容 q.prepare("SELECT * FROM table WHERE col LIKE :param ESCAPE '\\\\'"); // 处理用户输入关键词 QString keyword = edit->text().trimmed(); keyword.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_"); // 绑定带查询通配符的参数 q.bindValue(":param", QString("%%1%").arg(keyword)); // 必须先执行查询,确认成功后再传给模型 if (!q.exec()) { qDebug() << "SQL执行失败:" << q.lastError().text(); return; } SqlTableModel *model = new SqlTableModel(this); model->setQuery(q); // 不需要再调用model->select(),否则会覆盖已有查询结果 if (model->lastError().isValid()) { qDebug() << "模型加载失败:" << model->lastError().text(); return; } tableView->setModel(model);
注意:该方案下
QSqlTableModel的自动编辑回写、按字段排序等能力会受限,如果需要表格编辑功能请优先使用方案1。
核心安全规则
- 所有来自用户输入、外部接口的参数,永远不要通过
QString::arg()、字符串拼接的方式直接嵌入SQL语句或filter条件,必须使用参数绑定传值,Qt数据库驱动会自动完成特殊字符转义,彻底阻断SQL注入。 - 所有数据库操作必须增加错误捕获逻辑,通过
lastError()打印错误信息,避免出现无返回、无报错的黑盒问题。 - 使用LIKE查询时必须转义用户输入中自带的
%/_/\特殊字符,避免出现非预期的匹配结果。
内容的提问来源于stack exchange,提问作者user14773854
相关产品推荐
相关产品推荐

