前端拼接sql并发送后端执行等于彻底放弃防护,因用户可绕过前端校验直接构造恶意请求,后端若未强制参数化或白名单校验,将导致sql注入、数据泄露甚至删库;必须后端迁移至预编译查询,并对表名、字段名等结构参数严格白名单控制。

前端 JavaScript 拼接 SQL 并发送给后端执行,等于把数据库的解析权和执行权直接交到用户手里——这不是“不安全”,而是根本没设防。
前端拼接 SQL 的实际后果
你可能以为只是“把字符串发过去”,但只要后端不做校验、不强制参数化,fetch('/api/user', { method: 'POST', body: JSON.stringify({ sql: "SELECT * FROM users WHERE id = " + userInput }) }) 这种调用,就等同于在数据库前敞开大门。
常见错误现象:
- 用户输入
1 OR 1=1,后端直接执行SELECT * FROM users WHERE id = 1 OR 1=1,返回全部用户 - 输入
1; DROP TABLE users--,若后端用mysql2.query()执行多语句,表真被删 - 前端用模板字符串拼接字段名或表名(如
`SELECT * FROM ${tableName}`),后端未白名单校验就执行,租户隔离彻底失效
为什么不能靠前端过滤或“只传结构”
任何前端校验都可绕过:禁用 JS、抓包改请求、用 curl 直接 POST。所谓“前端只传字段名和值”,一旦后端动态拼接,风险照旧。
典型翻车场景:
- 前端传
{ table: 'logs_202605', condition: { level: 'error' } },后端写成db.query(`SELECT * FROM ${data.table} WHERE level = '${data.condition.level}'`) - 用
JSON.stringify()包一层,不代表 SQL 就安全了——后端反序列化后仍可能拼接 - 声称“只在内部系统用”,但内网扫描、员工终端中毒、SSO 泄露都会让攻击面扩大
迁移后端 + 参数化的实操要点
迁移不是简单把代码剪切粘贴到 Node.js 或 Python 里,关键在“如何绑定”。不同语言/驱动行为差异大,容易踩坑:
-
mysql2.query("SELECT * FROM u WHERE n = ?", [req.body.name])安全;但mysql2.query("SELECT * FROM u WHERE n = " + req.body.name)危险 - PostgreSQL 必须用
$1,$2占位符:client.query("SELECT * FROM logs WHERE level = $1", ["error"]);用?会报错 - MyBatis 中
#{}是参数化,${}是字符串替换——后者等价于拼接,禁用 - ORM 如 Sequelize 的
.raw()、SQLAlchemy 的text()需显式传参:db.session.execute(text("UPDATE logs SET status = :s"), {"s": status}),不能用 f-string
动态表名/字段名这类“不可参数化”的情况怎么处理
表名、列名、排序方向(ASC/DESC)、LIMIT 偏移量等,数据库本身不支持占位符,必须走白名单或严格正则校验。
例如:
- 允许切换的报表表名限定为
['sales_2025', 'sales_2026', 'users_active'],收到req.body.table后先if (!allowedTables.includes(tableName)) throw new Error('Invalid table') - 排序字段只允许
['id', 'created_at', 'name'],值来自枚举而非用户输入 -
LIMIT的数字必须是整数且有上限:Math.min(parseInt(req.query.limit) || 10, 100)
最容易被忽略的是日志记录环节:哪怕查询本身参数化了,如果把原始请求体(含恶意输入)原样记进数据库日志表,下一次“查日志”功能就可能触发二次注入——日志字段也要当数据处理,不能当 SQL 片段拼接。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南









