Node.js中URL参数存在SQL注入风险吗?如何安全处理?
问题描述
我正在学习Node.js,目前开发一个基于Express(使用EJS)的应用,该应用会根据用户访问的URL从MySQL数据库返回对应数据:
- 访问
http://localhost:3000/test会列出users表中的所有用户 - 访问
http://localhost:3000/test/3会返回ID为3的用户信息
我的/test路由代码如下:
app.get("/test", (req, res) => { let sql = "SELECT * FROM users ORDER BY name"; db.query(sql, function (err, results, fields) { if (err) logmessage(err.message); res.render("test", { data: results }); }); });
/test/:id路由的代码如下:
app.get("/test/:id", (req, res) => { var userId = req.params.id; let sql = "SELECT * FROM users WHERE id = " + userId; db.query(sql, function (err, results, fields) { if (err || !results.length) { res.redirect("/"); } else { res.render("test_user", { data: results }); } }); });
我之前做PHP开发时会使用预编译语句,想问:
- 上述写法是否安全?如果用户修改URL注入SQL代码,数据库会被入侵吗?
- 这将是线上应用,必须避免SQL注入,后续还要添加表单(使用
req.body)也需要做安全处理,Node是否有内置的预编译语句?
回答
现有代码的安全性分析
/test路由是安全的:该路由使用固定SQL语句,未拼接任何用户可控输入,不存在SQL注入风险。/test/:id路由完全不安全,存在严重SQL注入风险:直接将用户传入的userId拼接进SQL语句,攻击者可构造恶意id参数(比如3 OR 1=1、3; DROP TABLE users;--等),执行任意SQL语句,导致数据泄露、数据库被篡改甚至完全破坏,风险和PHP中直接拼接$_GET参数一致。
Node中防止SQL注入的正确方式
Node本身没有内置预编译语句,但常用的MySQL驱动(如mysql、mysql2包)都支持参数化查询(原理和PHP预编译语句一致),通过占位符自动处理参数转义,从根源上避免注入。
基础参数化查询写法
将SQL中用户输入部分用?作为占位符,再将参数以数组形式传入db.query():
app.get("/test/:id", (req, res) => { const userId = req.params.id; // 用?替代直接拼接的用户输入 const sql = "SELECT * FROM users WHERE id = ?"; // 参数作为数组传入query方法 db.query(sql, [userId], function (err, results, fields) { if (err || !results.length) { res.redirect("/"); } else { res.render("test_user", { data: results }); } }); });
多参数场景下,只需添加对应数量的?,保持参数数组顺序与占位符一致即可:
// 示例:多参数查询 const sql = "SELECT * FROM users WHERE id = ? AND name = ?"; db.query(sql, [userId, userName], (err, results) => { // 业务处理逻辑 });
表单(req.body)的安全处理
对于表单提交的req.body数据,同样遵循不直接拼接、全用参数化查询的原则,比如用户注册场景:
app.post("/register", (req, res) => { const { username, email, password } = req.body; const sql = "INSERT INTO users (username, email, password) VALUES (?, ?, ?)"; db.query(sql, [username, email, password], (err, result) => { // 业务处理逻辑 }); });
额外提示:可对用户输入做基础验证(比如检查id是否为数字、邮箱格式是否合法),但验证不能替代参数化查询,仅作为额外防护手段,核心防护必须依赖参数化查询。
补充:mysql2的async/await写法
如果使用支持Promise的mysql2包,可采用更简洁的async/await写法,参数化方式完全一致:
app.get("/test/:id", async (req, res) => { try { const userId = req.params.id; const sql = "SELECT * FROM users WHERE id = ?"; const [results] = await db.query(sql, [userId]); if (!results.length) { return res.redirect("/"); } res.render("test_user", { data: results }); } catch (err) { logmessage(err.message); res.redirect("/"); } });
内容的提问来源于stack exchange,提问作者Tibia Cams
相关产品推荐
相关产品推荐

