You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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开发时会使用预编译语句,想问:

  1. 上述写法是否安全?如果用户修改URL注入SQL代码,数据库会被入侵吗?
  2. 这将是线上应用,必须避免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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 08:20:31