Node.js Web应用/login页面泄露数据库所有用户数据的问题求助
紧急修复数据泄露问题
1. 立即移除危险的GET路由
你的app.get('/login')路由会无差别返回所有用户的完整数据(包括明文密码),这是核心泄露点。直接删除该路由,如果确实需要查看用户数据,必须做严格的权限控制(见下文)。
2. 修复POST登录接口的安全问题
当前POST接口存在两个严重问题:明文存储密码、返回敏感用户数据。修改方案如下:
- 禁止明文存储密码:使用哈希算法(如bcrypt)加密密码后再存入数据库。
- 不返回敏感数据:登录验证成功后只返回必要信息(如登录状态、用户名),绝不返回密码。
- 修正逻辑:当前逻辑会自动注册不存在的用户,这不符合常规登录流程,建议拆分出单独的
/register注册接口。
路由访问安全保障措施
- 身份验证:对敏感路由(如用户信息、后台接口)使用JWT(JSON Web Token)验证。用户登录成功后发放token,后续请求需在请求头携带token,服务器验证token合法性后才允许访问。
- 权限分级:区分普通用户和管理员权限,仅管理员能访问用户列表等敏感接口,普通用户仅能查看自己的信息。
- 密码安全规范:
- 使用bcrypt等慢哈希算法加密密码,避免使用MD5、SHA1等易破解的算法。
- 登录时,将用户输入的密码哈希后与数据库中的哈希值对比,而非直接比较明文。
- 输入校验:对用户输入的用户名、密码进行格式、长度校验,防止恶意输入。
- 强制HTTPS:部署应用时启用HTTPS,防止数据在传输过程中被窃听或篡改。
- 错误信息脱敏:不要将数据库错误、服务器内部细节返回给前端,统一返回友好的通用错误提示,避免泄露系统信息。
修正后的代码示例
首先安装依赖:
npm install bcrypt
修改后的核心代码:
const bcrypt = require('bcrypt'); const saltRounds = 10; // 哈希迭代次数,值越高越安全,性能消耗也越大 app.use(express.json()); app.use(express.urlencoded({ extended: true })); // 登录接口 app.post('/login', async (req, res) => { try { const { username, password } = req.body; // 校验输入完整性 if (!username || !password) { return res.status(400).send({ message: '用户名和密码不能为空' }); } // 查询用户(用户名应保证唯一,用findOne而非find) const user = await dbo.collection("data").findOne({ username }); if (!user) { return res.status(401).send({ message: '用户名或密码错误' }); } // 验证密码哈希 const isPasswordValid = await bcrypt.compare(password, user.password); if (!isPasswordValid) { return res.status(401).send({ message: '用户名或密码错误' }); } // 这里可添加JWT生成逻辑,示例: // const token = jwt.sign({ userId: user._id, username: user.username }, '你的密钥', { expiresIn: '1h' }); // res.send({ message: '登录成功', token, username: user.username }); // 临时返回基础成功信息 res.send({ message: '登录成功', username: user.username }); } catch (err) { console.error(err); res.status(500).send({ message: '服务器内部错误' }); } }); // 注册接口(单独拆分) app.post('/register', async (req, res) => { try { const { username, password } = req.body; if (!username || !password) { return res.status(400).send({ message: '用户名和密码不能为空' }); } // 检查用户是否已存在 const existingUser = await dbo.collection("data").findOne({ username }); if (existingUser) { return res.status(409).send({ message: '用户名已存在' }); } // 加密密码 const hashedPassword = await bcrypt.hash(password, saltRounds); await dbo.collection("data").insertOne({ username, password: hashedPassword }); res.send({ message: '注册成功' }); } catch (err) { console.error(err); res.status(500).send({ message: '服务器内部错误' }); } }); // 管理员专用用户列表接口(需加权限验证) app.get('/admin/users', async (req, res) => { // 此处需添加管理员权限验证逻辑(如JWT校验) // 示例: // const authHeader = req.headers.authorization; // if (!authHeader || !authHeader.startsWith('Bearer ')) { // return res.status(403).send({ message: '无权限访问' }); // } // const token = authHeader.split(' ')[1]; // 验证token并检查用户是否为管理员... try { // 查询时排除密码字段 const users = await dbo.collection("data").find({}, { projection: { password: 0 } }).toArray(); res.send(users); } catch (err) { console.error(err); res.status(500).send({ message: '服务器内部错误' }); } });
内容的提问来源于stack exchange,提问作者LeftClickMage
相关产品推荐
相关产品推荐

