预处理语句是防止SQL注入的唯一可靠防线,因其将SQL模板与参数彻底分离,数据库仅把绑定值视为数据而非代码,无论输入含何种恶意字符(如“admin' OR '1'='1”)均不会触发语法解析;其他过滤、转义或黑名单均为无效补丁。
必须用预处理语句,且禁止任何字符串拼接 SQL 的路径——这是唯一可靠防线,其他过滤、转义、关键字黑名单都是补丁,不是防御。
sqlite3_prepare_v2 能拦住注入它把 SQL 模板和数据彻底拆开:数据库先编译 "SELECT * FROM users WHERE name = ?" 这个结构,生成执行计划;之后你调用 sqlite3_bind_text 传入的值,只会被当作文本字节流塞进对应位置,不会触发语法解析。哪怕你传入 "admin' OR '1'='1",最终执行的仍是带一个问号参数的固定查询,' 和 OR 不会被当成 SQL 符号处理。
常见错误现象:sqlite3_exec 直接执行拼接字符串,或手动用 std::string::replace 替换引号——这两者都无效,因为攻击 payload 可能含 Unicode 绕过、嵌套注释、空字节等变体,靠字符替换永远漏。
?(位置)或 :name(命名),不支持 ${var} 或 %s
sqlite3_step 前完成,且每个 ? 必须且只能绑定一次std::string 出作用域),sqlite3_bind_text 第四个参数设为 -1 才安全(让 SQLite 自行拷贝)mysql_stmt_prepare 和 sqlite3_prepare_v2 的关键差异MySQL 的预处理要求显式声明参数类型,SQLite 则按绑定函数自动推断(sqlite3_bind_int vs sqlite3_bind_text)。这意味着:MySQL 中若用 mysql_stmt_bind_param 传错类型(比如把字符串当整数绑),可能触发截断或转换异常;SQLite 更宽松但更易掩盖逻辑错误——比如把时间戳字符串误用 bind_int,值变成 0。
立即学习“C++免费学习笔记(深入)”;
mysql_stmt_init,再 mysql_stmt_prepare,失败时检查 mysql_stmt_errno 而非全局 mysql_errno
sqlite3_prepare_v2 返回 SQLITE_OK 表示语法合法,不保证表/列存在——运行时才报错"SELECT * FROM ?" 是非法语法,这类场景必须靠白名单校验预处理语句对象(sqlite3_stmt* 或 MYSQ_STMT*)是资源,不是纯数据。没正确清理会导致句柄泄漏,尤其在异常路径下。
sqlite3_finalize 或 mysql_stmt_close
SQLITE_BUSY,MySQL 则静默丢弃step 后必须调用 sqlite3_reset(SQLite)或 mysql_stmt_reset(MySQL),否则后续绑定失效最常被绕过的点:开发者以为用了预处理就高枕无忧,却在日志记录、调试输出、缓存键生成等环节又把用户输入拼进字符串——只要有一处拼接,整条链路就崩。防注入不是某个函数的事,是整个数据流的设计约束。