字符串拼接sql危险因用户输入混入语句致sql注入,参数化查询通过分离结构与数据实现原生防护;node.js用?占位符、python flask用命名参数、java mybatis用#{}而非${}。

为什么字符串拼接 SQL 就是危险的
因为用户输入直接混进 SQL 语句里,数据库根本分不清哪段是逻辑、哪段是数据。比如用户传个 ' OR '1'='1,拼出来可能变成 SELECT * FROM users WHERE name = '' OR '1'='1',整张表就裸奔了。
参数化查询把「结构」和「数据」彻底分开:SQL 模板固定,参数由驱动层安全转义后送入执行引擎——这是数据库原生支持的安全机制,不是靠正则或过滤能替代的。
Node.js 中用 mysql2 做参数化查询的写法
别用 connection.query('SELECT * FROM t WHERE id = ' + req.query.id),这等于开门揖盗。
- ✅ 正确姿势:用
?占位符,参数以数组形式传入第二个参数,如connection.query('SELECT * FROM users WHERE id = ?', [req.query.id]) - ✅ 多参数用多个
?,顺序必须严格对应数组元素位置:query('INSERT INTO t(a,b) VALUES (?, ?)', ['x', 42]) - ❌ 不要手动拼接对象属性:
req.body.name不能直接插进 SQL 字符串,哪怕你 trim 过、转义过也不行 - ⚠️ 注意:
IN子句不支持单个?展开数组,得动态生成占位符,比如WHERE id IN (?, ?, ?)再传三个值
Python Flask + SQLAlchemy 里绕不开的坑
很多人以为用了 ORM 就自动防注入,其实不然——session.execute() 或原生 text() 查询如果拼接字符串,照样中招。
- ✅ 安全写法:用
session.execute(text("SELECT * FROM users WHERE name = :name"), {"name": request.args.get("name")}),用命名参数 + 字典传入 - ✅ 更推荐走 ORM 查询接口:
User.query.filter(User.name == request.args.get("name")).all(),它底层自动参数化 - ❌ 危险写法:
text(f"SELECT * FROM users WHERE name = '{request.args.get('name')}'"),f-string 一上,防线就垮 - ⚠️ 注意:
filter_by()只支持等值匹配且键名固定,动态字段名(比如用户选排序字段)不能用它,得走text()+ 参数化,别偷懒
Java MyBatis 的 #{} 和 ${} 到底差在哪
#{} 是预编译参数占位符,MyBatis 会把它转成 JDBC 的 ?;${} 是纯字符串替换,连引号都不加,拼完直接进 SQL——这就是绝大多数 MyBatis 注入漏洞的根源。
- ✅ 所有用户可控输入,一律用
#{},比如WHERE username = #{username} - ❌ 禁止在
${}里放任何外部输入,像ORDER BY ${sortBy}是高危操作,必须白名单校验后硬编码 - ⚠️ 特殊场景如动态表名、列名,无法参数化,只能做严格白名单控制:
if (!Arrays.asList("users", "orders").contains(tableName)) throw new IllegalArgumentException(); - ? 提示:开启 MyBatis 的
logImpl=STDOUT_LOGGING,看实际执行的 SQL,能快速识别出哪些地方偷偷用了${}
真正难的不是写对一行参数化代码,而是所有入口——搜索、导出、排序、条件组合——都得绷着这根弦。漏掉一个 ${},或者一次手抖的字符串拼接,整个防护就形同虚设。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










