应使用参数化查询而非检测sql关键字,因黑名单式过滤易被绕过;c++需依赖数据库库的绑定接口如sqlite3_bind_text;预检仅适用于日志审计且不能替代参数化。

直接检测 SQL 注入关键字不可靠
单纯用 std::string::find 或正则匹配 "SELECT"、"UNION"、"--" 这类字符串,几乎没用。攻击者可以大小写混用("sElEcT")、加空白符("SE/* */LECT")、用编码绕过("%27"),甚至利用数据库特定语法(如 MySQL 的 /*!50000SELECT*/)。这种“黑名单式”过滤在 C++ 里写得再全,也挡不住真实攻击。
应该用参数化查询,而不是自己写检测逻辑
C++ 本身不提供内置 SQL 执行能力,实际是否安全取决于你用的数据库客户端库。关键不是“检测字符串”,而是“避免拼接 SQL”。例如:
- 用
sqlite3_bind_text绑定参数,而非sqlite3_exec拼接字符串 - 用
libpqxx的transaction::exec_params,传std::string作为参数值,不是拼进 SQL 字面量 - 用
ODBC的SQLBindParameter,把用户输入当SQL_C_CHAR参数绑定
只要走参数化路径,"'; DROP TABLE users; --" 就只是个普通字符串,数据库不会解析执行它。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
如果真要预检(仅限日志/审计场景)
某些内部系统需要对原始输入做粗筛(比如记录可疑请求),这时可做轻量级启发式检查,但必须明确:这不能替代参数化,也不能用于拦截决策。
- 统一转小写后再查关键词,覆盖常见大小写变体
- 只检查明显危险模式,如连续多个
'、"、--、/*,或SELECT紧跟空格/换行后出现FROM - 避免正则回溯爆炸:用
std::string_view遍历扫描,不用std::regex匹配复杂模式 - 示例片段(仅示意):
bool has_obvious_sql_frag(const std::string& s) { auto lower = to_lower(s); // 自行实现简单转小写 return lower.find("select ") != std::string::npos || lower.find("union ") != std::string::npos || lower.find("--") != std::string::npos || std::count(lower.begin(), lower.end(), '\'') > 5; }
别碰 HTML 实体解码和 URL 解码再检测
有人想先对输入做 url_decode 或 html_entity_decode 再查关键词,这是错的。解码时机由业务决定——前端提交的 %27 是不是该被当成单引号,取决于你如何使用它。若最终是进 SQL 参数,就不该提前解码;若用于显示,则应按输出上下文转义,而非用来“检测注入”。混淆编解码边界,反而制造新的漏洞点。
真正难的不是写几个 find,是厘清数据流向:输入 → 解码 → 验证 → 绑定 → 执行 → 输出转义。每个环节职责不同,混在一起就容易漏掉一环。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










