sql注入防护应使用预编译语句而非删字符,如mysqlcppconn调用setstring()、sqlite3用bind_text()、orm默认参数化;日志等非sql场景才可白名单过滤。

SQL注入防护不能靠“删字符”
直接删除所谓“不安全SQL字符”是危险且无效的做法。SQL注入的本质是语义拼接失控,不是字符黑名单能解决的。比如 ' 在某些上下文里完全合法(如插入姓氏 O'Connor),而 1=1 这类字符串根本不会出现在原始输入里,却可能由拼接后生成。硬删字符会导致数据损坏、编码错乱,还给人“已防护”的错觉。
应该用预编译语句(PreparedStatement)
C++ 没有内置数据库驱动,实际方案取决于你用的库,但核心原则统一:把参数和SQL模板分离,交由驱动做类型化绑定。常见组合如下:
- 使用
mysqlcppconn(MySQL Connector/C++)时,调用sql::PreparedStatement::setString()等方法传参,底层自动转义并以二进制协议发送 - 使用
sqlite3C API 时,用sqlite3_bind_text()或sqlite3_bind_parameter_index()绑定值,而非sqlite3_exec()拼接字符串 - 使用 ORM 如
ODB或SQLiteCpp,所有查询方法默认走参数化,只要不手动拼std::string到 SQL 字符串里就安全
如果真要过滤(仅限日志/显示等非SQL场景)
某些场合如写审计日志、前端回显,需清理控制字符或避免 XSS,这时可做白名单过滤,但和防SQL注入无关。示例(C++17):
std::string sanitize_for_log(const std::string& s) {
std::string out;
for (char c : s) {
if (c >= 0x20 && c <p>注意:<code>std::regex_replace</code> 性能差、易出错,不推荐用于高频路径;UTF-8 多字节字符需用 <code>std::codecvt_utf8</code> 或第三方库(如 ICU)处理,否则会截断字节。</p><h3>最常被忽略的坑:ORM 自定义查询和字符串格式化</h3><p>很多团队误以为用了 ORM 就绝对安全,结果在以下地方翻车:</p>
- 手写原生 SQL 时用
fmt::format("SELECT * FROM user WHERE id = {}", id)—— 这和拼接"..."+std::to_string(id)一样危险 - ORM 的
raw_query()或execute_sql()方法,传入的 SQL 字符串若含用户输入,必须确保参数通过占位符(如?或:id)绑定,而非字符串替换 - 动态构建 WHERE 条件时,用
std::string拼接字段名(如"WHERE "+field+" = ?")—— 字段名无法参数化,必须严格白名单校验
真正难的从来不是“删哪些字符”,而是识别出哪一行代码正在把用户输入当作 SQL 结构的一部分。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











