SQL result lengths and showing nothing when searched - mysql

I want my Discord bot to check if a user that joined exists in a MySQL table. However, when it sends the query, it's basically telling me that it doesn't exist even though it should.
This is my current code:
bot.on('guildMemberAdd', async (member) => {
console.log(member.id)
let query = `SELECT userId FROM QR5PVGPh1D.users WHERE userId = '${member.id}'`
let result = connection.query(query)
if(result.length > 0){
console.log("It works!")
}
})

Node is asynchronous, so here you try to console.log result before it has been populated...
You'll find more info in this here :
Node JS MySQL query function not returning result
How do I return callback of MySQL query and push to an array in Node.js?
Here is the code with a callback function. Try it, it should work:
let query = `SELECT userId FROM QR5PVGPh1D.users WHERE userId = '${member.id}'`
connection.query(query, function (err, result) {
if (!err) {
if (result.length > 0) {
console.log("It works!")
}
}
});

Explanation:
As BadSpencer has stated, the mysql driver is asynchronous, and based around callbacks.
Say you're planning on picking your friend up to go to a sporting event. You're not sure when they want you to come, so you call them on the phone and ask them. They think about it for a while, and then tell you a time. You got the information you requested, so you hang up. In programming terms, this would be an example of synchronous code (sometimes thought of as "normal" code in Node.js).
Put yourself back in the same situation. However, when you call your friend this time, they're very busy. You don't want to bother them so you ask them to call you later. You hang up, but now you wait. An hour later, they call you back and tell you the time. This is the thought process of asynchronous code.
There's a lot more that goes on behind the screen, but for simplicity's sake, I'm not going to bombard you with all that information in this answer.
Solutions:
You should pass a function to act as a callback which will use the returned data. Consider this example:
let query = `SELECT userId FROM QR5PVGPh1D.users WHERE userId = '${member.id}'`;
// Passing the callback function as the second parameter.
connection.query(query, (err, result) => {
if (err) return console.error(err);
if (result.length > 0) console.log('It works (it actually does).');
});
However, this callback-based nature can become a nightmare due to the scope of the result and subsequent flow of the code. After a few queries, your code can become messy. To prevent this, you can wrap the query in your own Promise (or use a Promise-based version of the mysql package, like promise-mysql) and await the calls.
Here's an example setup:
// Defining 'connection' as a parameter so
// you can export this function and use it
// anywhere.
function query(connection, sql) {
return new Promise((resolve, reject) => {
connection.query(sql, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}
// Asynchronous context needed for 'await' (this needs to be within an async function).
let query = `SELECT userId FROM QR5PVGPh1D.users WHERE userId = '${member.id}'`;
let result = await query(connection, query)
.catch(console.error);

Related

Preventing SQL Injection from node.js using mysql driver with multipleStatements: true

I've been developing an "Employee leave management" web app project for our internal use using node.js with express and ejs template. Now, my employer wants me to make the app accessible through internet and I'm worried about SQL injection.
Let's say I have a button like this in html:
Edit
This will GET from index.js file:
const { edit } = require("./request");
app.get("/edit/:ReqID", edit);
This will then go to module edit in request.js file:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
let squery = `SELECT * FROM table1 WHERE ReqID="${ReqID}";
SELECT * FROM table2 WHERE ReqID="${ReqID}";`;
db.query(squery, function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
There might be two or more queries in there and I'm using mysql driver for node.js with multipleStatements: true and I'm aware of warning "Support for multiple statements is disabled for security reasons (it allows for SQL injection attacks if values are not properly escaped)." This will return something like http://localhost:port/edit/reqid on the browser address box. I saw a video from youtube that says SQL Injection can be done through the browser's address box like http://localhost:port/edit/reqid;";SELECT * FROM users; so I did that and for sure I can see that syntax being send to the server. So I follow the suggestion in the video to do a placeholder like this:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
let squery = `SELECT * FROM table1 WHERE ReqID= ?;
SELECT * FROM table2 WHERE ReqID= ?;`;
db.query(squery, [ReqID, ReqID], function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
Then I try the extreme http://localhost:port/edit/reqid;";DELETE FROM users; and http://localhost:port/edit/reqid;";DROP TABLE users; separately and it works! First it deletes data from users tble and for sure the second drop table command also worked. After the first attempt, I refresh the browser with the same sql injection syntax and I've got this message:
{"code":"ER_BAD_TABLE_ERROR","errno":1051,"sqlMessage":"Unknown table 'users'","sqlState":"42S02","index":1,"sql":"SELECT * FROM table1 WHERE ReqID= "ReqID;";drop table users;";SELECT * FROM table1 WHERE ReqID= "ReqID;";drop table users;";"}
So, the table users clearly have been dropped from the database.
Update:
I did further testing based on the information I gained from this answer and I did something like this:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
db.query(`SELECT * FROM table1 WHERE ReqID= ?; SELECT * FROM table2 WHERE ReqID= ?;` , [ReqID, ReqID], function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
Then I re-test with multiple variation of http://localhost:port/edit/reqid;";DROP TABLE users; (double quote in between)
http://localhost:port/edit/reqid;';DROP TABLE users; (single quote in between) etc. and it doesn't seem to be dropping the table anymore. However, I still see the statement being sent to the server so I'm still wary of the DROP syntax being effective somehow.
Update 2:
Note: Fortunately, the deployment has been delayed and I have more time to sort out the issue.
After researching for a while, taking the comments into consideration and testing multiple method, I came up with this structure:
function(req, res) {
let dcode = [req.body.dcode];
let query1 =`SELECT col1, col2 FROM table1 WHERE DCode=?`;
db.query(query1, dcode, function(err, result_1) {
if (err) {
return res.status(500).send(err);
}
let query2 =`SELECT col1, col2 FROM table2 WHERE DCode=?`;
db.query(query2, dcode, function(err, result_2) {
if (err) {
return res.status(500).send(err);
}
res.render("login.ejs", {
result1: result_1,
result2: result_2
});
});
});
}
Which is simple enough and no major change to my current codes. Would this be sufficient to prevent SQL injection in node.js?
Allowing multi-statement strings, itself, invites SQL injection. So, avoid it.
Plan A:
Consider ending an array (perhaps in JSON) to the server; let it then execute each statement, and return an array of resultsets.
But it would be simpler to simply issue the statements one at a time.
(If the client and server are far apart, the one-statement-at-a-time method may cause a noticeable latency.)
Plan B:
Build suitable Stored procedures for any multi-statement needs. This, where practical, avoids multi-statement calls. And avoids latency issues (usually).
Here are a few suggestions that might help:
Never use template strings like this: Select * from table where id = ${value}. SQL injections will happen - 100%!. Instead you should use build in driver defense mechanism. Like this: query('Select * from table where id = ?', [value]). This should prevent SQL injection.
Use single statements per query. If you need to do multiple operations in one request to database - consider creating stored procedure. Stored procedures also have build in security mechanism.
Consider using query builder or ORM. They also have additional layer of security on top of build in driver one.
You could also explicitly escape SQL string with help of 3rd party library.

Node.js doesn't execute code after MySQL Query

I have no idea what I did wrong, it's supposed to output onto console "Query Initiated" after it grabs the result but nothing is logged and I have no idea what I did wrong. Yes, I know the syntax is ugly, I ran a prettifier over it and now it is incredibly ugly and I am too lazy to manually go through 200+ lines of code to fix it.
connection.query(`SELECT * FROM pedodb WHERE ID='${msg.author.id}'`),
function (err, result) {
query.on('result', function (err2, result2) {
callback(null, rows, fields);
console.log("Query Initiated")
The callback should be associated with the query function.
And the practice of using parameters is that they should be in separate parameters otherwise there are chances that it can lead to SQL injection.
connection.query(`SELECT * FROM pedodb WHERE ID = ?`, [msg.author.id],
function (err, result, fields) {
callback(null, rows, fields);
console.log("Query Initiated")
})

Nodejs passport setup confusion

I have been trying to setup my Nodejs MySQL database configuration. I found this passport.js config for MySQL on Github. The config works properly but there is a part that I do not understand.
var insertQuery = "INSERT INTO users ( email, password ) values ('" + email +"','"+ password +"')";
console.log(insertQuery);
connection.query(insertQuery,function(err,rows){
newUserMysql.id = rows.insertId;
return done(null, newUserMysql);
});
I am confused about the insertID field. The table I am using does not have a field called insertID. It does however have a field named ID. I tried changing that line to
newUserMysql.id = rows.Id;
bu doing so gives me:
Error: Failed to serialize user into session
Leaving it as it is gives me no error
Looks like insertID has nothing to do with the ID field of my table but I do not understand what it means
That probably represents LAST_INSERT_ID() which is the ID of the last row inserted.
The response of an INSERT is not "rows" but a result object, so maybe better named it'd be:
connection.query("...", function(err, result) {
newUserMysql.id = result.insertId;
return done(null, newUserMysql);
});
It's important to note that using Promises dramatically simplifies your code, and async/await can take that even further. This could be as simple as:
let result = await connection.query("...");
newUserMysql.id = result.insertId;
return newUserMysql;
Where that's inside an async function with a Promise-driven database library like Sequelize. You're not handling the potential errors in your first case. In the second you'll get exceptions which will wake you up when there's problems.

Error from ending connection from 'query inside query'

I am using mysql node module and having an error of Error: Cannot enqueue Query after invoking quit. from my source code which has a block that does 'update if exists, otherwise insert' (similar logic as in another question I asked).
The reason I include it in node is that the definition of 'duplicates' is customized.
I have tested my source code and it was able to successfully perform 'update if exists, otherwise insert' to my table, but I am not able to close my connection, as for my insert/update query reside in the search query.
const insertQueue = (results) => {
_.forEach(results, (values, key) => {
_.map(values, (value) => {
let query = 'SELECT index FROM table WHERE (document=? AND ((document NOT like \'PERSONAL:%\' AND username=?) OR (document like \'PERSONAL:%\' AND serial=?)))';
connection.query(query, [value.document, key, value.serial], function(error, rows){
if (error) {
console.log(error);
}
else {
//Ideally there should be only one entry, but still return an array to make the function error-safe
let indices = _.map(rows,'index');
if (_.isEmpty(indices)) {
//sqlInsert and sqlUpdate are defined elsewhere and has sql query defined with 'connection.query(..)' similar to this
sqlInsert(key, value.serial, value.document, value.lastRun);
}
else {
_.each(indices, (index) => {
sqlUpdate(index,value.lastRun);
});
}
}
});
});
});
connection.end();
}
sqlInsert and sqlUpdate are defined elsewhere and has sql query defined with connection.query(..) similar to the function above.
I understand that connection.query(..) is asynchronous, and I put my connection.end() in the very end of the function. But I don't understand why I am still getting the error of Cannot enqueue Query after invoking quit every time I call my insertQueue.
Also if I replace my sqlInsert(...); and sqlUpdate(...) with some testing command (with no db sql query execution), the error will be gone.
Is there any reasons for this?
first thing you should not call async function inside loop (map, forEach, for, while or anyone).
second it's worst idea to fire query in loop.
mysql.js provide method for bulk insert
from official doc https://github.com/mysqljs/mysql
Nested arrays are turned into grouped lists (for bulk inserts), e.g.
[['a', 'b'], ['c', 'd']] turns into ('a', 'b'), ('c', 'd')
take an example
connection.query('INSERT INTO posts SET ?', [['a', 'b'], ['c', 'd']], function (error, results, fields) {
if (error) throw error;
// ...
});
third i think lastRun is datetime. you can choose mysql type current timestamp and now no need to worry this in query,

Get data from MySQL database and pass it to jade an access it using nodejs

I have started nodejs to make a simple chat application in it.
For this I want to access all users which are in mysql database.
My code is like:
JS
exports.authenticate = function(req, res) {
//connection.connect();
var sql="SELECT * from users where username='"+req.body.user+"' and password='"+req.body.pass+"' LIMIT 1";
connection.query(sql, function(err, rows, fields) {
if (err) throw err;
//res.send('Your data is: ', rows);
var str="Hi, <b>"+rows[0].name+"</b> ("+rows[0].email+")";
sql="SELECT DISTINCT username,name from users ORDER BY name";
connection.query(sql, function(err, datarows, fields) {
if (err) throw err;
//res.send('Your data is: ', rows+' <br/> All Users are : ', datarows.length+"<a href='/'>Login</a>");
str+='<ul style="list-style:none;width:300px">';
for(var index=0;index<datarows.length;index++)
{
str+="<li><a href='javascript:;'>"+datarows[index].name+", "+datarows[index].email+"</a></li>";
}
str+='</ul>';console.log(str);
console.log(str)//gives ul
});
str+="<a href='/'>Login</a>";
res.send(str);//this not gives the ul of other users
});
}
The above code has problems that I wrote console.log(str)//gives ul this prints the whole string like Hi, <b>Rohan</b> ("rohan#xyz.com")<ul><li><a>Something</a></li>....</ul>. But res.send(str); sends only Hi, <b>Rohan</b> ("rohan#xyz.com")<a href='/'>Login</a>.
Why this is happening?
Is my str variable not global?
Can I use res.send() many times, if yes then how?
Can I use the above code with jade then what code should I write for this.
I found this answer How to pass data form mongodb (using Mongous module) into nodejs view (using temp engine jade)? related to my problem
app.get('/', function(req, res) {
mgs(dbColl).find(function(data){
res.render('yourview.jade', { data: data });
});
});
Then,How to access data in yourview.jade
I also want to know for which type of applications we should use nodejs ?
Thank in advance.
This is happening because Node is asynchronous. Your res.send(str) is executed immediately after calling your inner connection.query(), therefore the callback which adds more hasn't executed before the send.
No, your str variable is not global.
I do not believe so. If you want to write to the response before sending it, you can use res.write().
Yes you can use Jade instead of what you're doing now. Take a look at the Jade docs. They are full of examples of variables being used in jade. To summarize it for you though, you should be able to access a variable with #{variable}.
As for your last question, I'm really not sure what you're asking. You can use node for alot of stuff, from controlling quadcopters to web sites and services.