MYSQL query gives same output as input value in nodejs - mysql

I'm having trouble sending a secure SQL query to mysql server from nodeJS.
When using this piece of code:
let test = "chart1yusd"
db.query('SELECT '+ test +' FROM coinDetail WHERE id = ?',[
requestID,
],function(err, result){
console.log(result)
res.send(result);
});
I get the correct output:
[ RowDataPacket {
chart1yusd:
'[[1589846400000,0.118103090573034],[1590710400000,0.14990946026516133],[1591574400000,0.13832947332698067],[1592438400000,0.14626382998803866],[1593302400000,0.12312689681792738],[1594166400000,0.13064585929472963],[1595030400000,0.15134667446052835],[1595894400000,0.14511870584962466],[1596758400000,0.2044381065518002],[1597622400000,0.27718349745013865],[1598486400000,0.24733539468353966],[1599350400000,0.15428765583919232],[1600214400000,0.18333082011361068],[1601078400000,0.16554945200142196],[1601942400000,0.15536379703562367],[1602806400000,0.17817122591867382],[1603670400000,0.14901182983793498],[1604534400000,0.15243756831164262],[1605398400000,0.25106271236512906],[1606262400000,0.22676917209412703],[1607126400000,0.22559988488004115],[1607990400000,0.3198349358258863],[1608854400000,0.28175278764129286],[1609718400000,0.48270197854575086],[1610582400000,0.5562085890730089],[1611446400000,0.4835010798224596],[1612310400000,0.46142579899847125],[1613174400000,0.7327130188547617],[1614038400000,0.7803392989162374],[1614902400000,1.2216997730172996],[1615766400000,1.1508817751759253],[1616630400000,1.2024881678362118],[1617494400000,1.1159947150076852],[1618358400000,2.3093588705698713],[1619222400000,1.9654124655802336],[1620086400000,2.0674879115219373],[1621230497000,1.3424936470400413]]' } ]
But when using this piece of code (the more secure version against sql injection):
let test = "chart1yusd"
db.query('SELECT ? FROM coinDetail WHERE id = ?',[
test, requestID,
],function(err, result){
console.log(result)
res.send(result);
});
I get this output, and not the data I want like before.
[ RowDataPacket { chart1yusd: 'chart1yusd' } ]
What did I do wrong and how can I fix this?

The query SELECT ? FROM coinDetail WHERE id = ? is not a valid MySQL prepared statement, because only literal values can be represented by ? placeholders. Database objects, including column and table names, can't have placeholders.
So, you are basically stuck using your first version. If you know a priori that the chart1yusd column is what you want to select, then just hard code it:
SELECT chart1yusd FROM coinDetail WHERE id = ?

You cannot use placeholders for columns, only for values. To dynamically select the column of interest, just select all columns, and then pick the one you need:
let test = "chart1yusd"
db.query('SELECT * FROM coinDetail WHERE id = ?', [
requestID,
], function(err, result){
console.log(result[0][test])
res.send(result[0][test]);
});

Related

Node.js rounds my sql result. how do i prevent this?

I am requesting a bigint from my database. However, Node.js alters the value of my request, so that my program doesn't work.
Here's my code:
let query = `SELECT transcriptchannel FROM guilds WHERE guild_id = 933323734322913301;`
sql.query(query, function(err, result){
if(err) throw err;
console.log(result);
}
The console logs:
[ RowDataPacket { transcriptchannel: 946134226196123600 } ]
However, if i run the same statement in PHPmyAdmin, it looks like the following:
SELECT transcriptchannel FROM guilds WHERE guild_id = 933323734322913301;
it returns:
946134226196123658
Why does Node.JS round the value up and how do i prevent it?
This happens when the number is greater than Number.MAX_SAFE_INTEGER.
Please try following and see if you are getting expected value:
const sql = mysql.createConnection({supportBigNumbers: true, bigNumberStrings: true});
reference - https://github.com/mysqljs/mysql

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.

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.

NodeJs and MySQL ER_BAD_FIELD_ERROR

I have a problem I need help with a question. So the problem is I have a route to display all rows of my students table and it works, but know when I try the code below to search for a specific student_id I get:
"Failed to query for users: Error: ER_BAD_FIELD_ERROR: Unknown column 'undefined' in 'where clause'"
I have tried the queryString as:
My table has student_id, firstName, lastName, checkIn, and checkOut as columns.
They are all VARCHARS.
queryString = SELECT * FROM students WHERE student_id = ${id}
queryString = SELECT * FROM students WHERE student_id = ? and then getConnection().query(queryString, [id], (err, rows, fields) => {
But I get the same error, I would really appreciate the help.
The question that I have would be implementing after all my different search routes work as intended to display the data nicely in html, but I'm new to this and I can't find anything to help me with this problem. I would like it to display as a list almost like MySQL displays it. Is that possible? I'm using the ejs view engine as my html pages.
// Route to search by id
router.get("/searchById", (req, res) => {
const id = req.body.id
queryString = `SELECT * FROM students WHERE student_id = ${id}`
getConnection().query(queryString, (err, rows, fields) => {
// If error occures
if(err) {
console.log("Failed to query for users: " + err)
res.sendStatus(500)
return
}
if(rows) {
console.log(rows)
console.log("We fetched Students successfully")
res.json(rows)
}
})
})
For GET request you should use req.query.id
For post request you should use req.body.id
In your code you have used get request and tried to fetch req.body.id

Escape question mark characters as placeholders for MySQL query in NodeJS

I have the following query that I'm using in the query function (https://github.com/mysqljs/mysql#escaping-query-values) :
database.query("UPDATE Project SET Content = 'Hello ?' WHERE Id = ?", [id], function(err, rows, fields) {
if (err) {
console.log(err);
res.json({ status: 'FAIL QUERY' });
} else {
res.json({ status: 'SUCCESS' });
}
});
I have an error because it is replacing the question mark on "'Hello ?'" and "WHERE Id = ?" and he only have one attribute : [id].
How to avoid this ?
You have various options:
database.query("UPDATE Project SET Content = ? WHERE Id = ?", [ 'Hello ?', id], function(err, rows, fields) { ... })
Or:
database.query("UPDATE Project SET Content = 'Hello ?' WHERE Id =" + mysql.escape(id), function(err, rows, fields) { ... });
I would report this as a bug to that project. They should not replace the ? characters when those character appear:
Inside string literals, as in your example.
Inside delimited identifiers. It's uncommon, but legal SQL, to use punctuation symbols as part of a table name or column name.
Inside comments.
If that Node.js project is replacing the ? regardless of context, then this is an unacceptable bug.
I would stop using that package until they fix the bug.
Thanks for all your answer ! I learned a lot.
The robertklep answer using (mysql.escape(id)) worked for me.
However I found another way that I'm sharing with you for adding multiple parameters to the query :
You can create a JSON called queryJSON and adding to it key - value. Ex : queryJSON = {Content: 'Hello ?', Address: '742 Evergreen Terrace'}. And then :
database.query("UPDATE Offer SET ? WHERE Id = ?", [queryJSON, id], function(err, rows, fields) {
This is usefull for my put request for adding one or more paramters to the request.
Prepared statements will replace each instance of ?, in order, with the elements in the passed array, also in order. You will need to use a different placeholder character if you want to keep it as a placeholder, or have 2 elements in the array, the first being what the ? after Hello will be replaced with (bear in mind that it will also put single quotes around that element) and the second one for Id = ?
Preventing SQL injections
Escape question mark characters as placeholders for MySQL query in NodeJS is used to prevent SQL injections (Example 1). You can use escape function to do the same (Example 2).
To prevent SQL injections, you should use escape function the values when query values are variables provided by the user.
Example 1: Escape query values by using the placeholder ? method
var adr = 'Mountain 21';
var sql = 'SELECT * FROM customers WHERE address = ?';
con.query(sql, [adr], function (err, result) {
if (err) throw err;
console.log(result);
});
Example 2: Here we used escape function to avoid SQL injections
var adr = 'Mountain 21';
var sql = 'SELECT * FROM customers WHERE address = ' + mysql.escape(adr);
con.query(sql, function (err, result) {
if (err) throw err;
console.log(result);
});
More details