Socket.io执行SQL查询遇SQLITE_RANGE错误,无法获取用户密码
问题:SQLite查询用户密码时出现SQLITE_RANGE: column index out of range错误
问题现象
- 基于Socket.io开发时,根据用户输入的用户名查询对应密码,触发数据库错误
- 错误信息:
Database {} error looking up if user exists Error: SQLITE_RANGE: column index out of range - 可以正常生成哈希密码
hashedPassword,但无法通过getPassword语句获取数据库中的密码 - 即使将
socket.username替换为数据库中存在的测试用户名,错误依然发生,排查后锁定问题在数据库查询环节
相关代码
app.js
// we store the username in the socket session for this client socket.username = validator.escape(username); socket.password = validator.escape(password); const salt = 'Some salt for the hash'; const hashedPassword = crypto.pbkdf2Sync(socket.password, salt, 1000, 64, 'sha512').toString('hex'); db.serialize(function() { var checkIfUserExists = "select count(*) from users where username = '" + socket.username +"'"; var getPassword = `SELECT password FROM users WHERE username = "${socket.username}"` console.log(db.get(getPassword)) db.get(checkIfUserExists, getPassword, function(err, row) { if (err) { console.error('error looking up if user exists', err); return; } if (row['count(*)'] === 0) { // console.log("user doesn't exist!"); socket.emit('alertuserdoesntexists'); } if (getPassword === hashedPassword) { console.log("virker") socket.emit('redirect'); }})
chat.js
var $usernameInput = $('.usernameInput'); // Input for username username = $usernameInput.val(); password = $passwordInput.val(); // Tell the server your username socket.emit('existing user', username, password) });
问题分析
db.get()调用错误:你把getPassword这个SQL语句作为第二个参数传给db.get(checkIfUserExists, ...),但db.get()的第二个参数应该是当前查询的参数值数组,不是另一条SQL,这直接导致SQLite解析参数时索引越界,触发SQLITE_RANGE错误。- SQL注入风险:直接将用户名拼接进SQL语句,不仅可能因为特殊字符导致语法错误,还存在严重的SQL注入漏洞。
- 密码对比逻辑错误:你试图把SQL语句字符串
getPassword和哈希密码hashedPassword对比,这完全是逻辑错误,应该用数据库查询到的密码哈希值和生成的哈希值对比。
修复后的代码(app.js)
// 存储用户名到socket会话 socket.username = validator.escape(username); socket.password = validator.escape(password); const salt = 'Some salt for the hash'; const hashedPassword = crypto.pbkdf2Sync(socket.password, salt, 1000, 64, 'sha512').toString('hex'); db.serialize(function() { // 参数化查询:用?作为占位符,避免SQL注入 const checkIfUserExists = "SELECT count(*) as userCount FROM users WHERE username = ?"; // 第一步:检查用户是否存在 db.get(checkIfUserExists, [socket.username], function(err, row) { if (err) { console.error('查询用户是否存在时出错', err); return; } if (row.userCount === 0) { socket.emit('alertuserdoesntexists'); return; } // 第二步:用户存在,查询对应的密码哈希 const getPassword = "SELECT password FROM users WHERE username = ?"; db.get(getPassword, [socket.username], function(err, passwordRow) { if (err) { console.error('查询用户密码时出错', err); return; } // 对比数据库中的哈希值和生成的哈希值 if (passwordRow.password === hashedPassword) { console.log("验证通过"); socket.emit('redirect'); } else { // 可选:添加密码不匹配的提示 socket.emit('alertpasswordincorrect'); } }); }); });
修复说明
- 修正
db.get()调用方式:每个查询单独调用db.get(),参数用数组传递,解决参数索引越界问题 - 使用参数化查询:用
?占位符替代直接字符串拼接,彻底避免SQL注入 - 修复密码对比逻辑:从数据库获取真实的密码哈希值后,再和生成的
hashedPassword对比 - 拆分查询逻辑:先确认用户存在,再查询密码,代码逻辑更清晰易维护
内容的提问来源于stack exchange,提问作者Wager2000
相关产品推荐
相关产品推荐

