Escape question mark characters as placeholders for MySQL query in NodeJS - mysql

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

Related

MYSQL query gives same output as input value in nodejs

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]);
});

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.

How to do multiple mysql queries in one line in Node.js

I'm trying to get results from multiple tables with one request from client. I first tried JOIN, but that would mean I'd have to have multiple rows that contain duplicate information. Then I decided to do a different query for each table separately, although I'm not sure know how cost efficient this is.
How should I construct the query so that it is accepted by Node.js? The one I'm trying to use now doesn't work if I don't use quotation marks to wrap the whole query, but if I do use them it complains there is an error with my query.
Here is the code for the query.
sql = "'SELECT * from `Ilmoittautuminen` WHERE `optunnus` LIKE ' + mysql.escape(reg.body.optunnus); 'SELECT * from `Jasenmaksu` WHERE `optunnus` LIKE ' + mysql.escape(reg.body.optunnus); 'SELECT * from `OppOikeus` WHERE `optunnus` LIKE ' + mysql.escape(reg.body.optunnus);"
console.log(sql);
connection.query(sql, function(err, rows){
console.log(rows)
if(err){
console.log(err)
res.json({
success: false,
message: err,
});
}else{
queryResponse(res, rows);
}
});
taking reference of https://github.com/mysqljs/mysql
passs multipleStatements:true in your connection param
and try with an exmaple
conn.query(`
SELECT * FROM posts limit 1;
SELECT * FROM Users limit 1;
`, (err, results)=>{
console.log(err, results)
});

Node.js, After inserting not shown in list query

After adding a product to the mysql database, I want to dump all the products with the latest product. This product is being added with an algorithm to the database and I want to list all the products immediately afterwards. Already tried "async", "promise" etc.
--When the table is empty--
connection.query("INSERT INTO `products` (id, name, price)", function (error, results, fields) {}); //inserted one row
connection.query("SELECT * FROM `products`", function (error, results, fields) {}); // show only []
after second insertion list query show only first row but not second. The main problem is this and table has two rows.
Thank you.
Query data when insert is done:
connection.query("INSERT INTO `products` (id, name, price)",
function (error, results, fields) {
connection.query("SELECT * FROM `products`", function (error2, results2, fields2){});
});
connection.query("INSERT INTO products(id, name, price)",
function (error, results, fields) {
if(error) return ....
else{
connection.query("SELECT * FROM products", function
(error,results,fields2){
var returned_data = results;
console.log(results);
//res.send(results);
})
}});
Would be the way to go, but check whether you're inserting the data the right way at all, since you haven't provided the way you do it, I doubt that any async method would fail you itself.
EDIT on request: You can pass the results to a variable, but you can only use it inside that function if you don't (because of its scope) , ie res.send or res.end it (if you're using this inside a request, which I'm guessing you are), or console.log it or just write it to a file.

node.js and mySQL issue

I am running a query which gives me confusing results. I have a node.js server which takes some user input from a form on the client side and uses socket.io to get the input on the server. here is the function which runs after receiving user input
databaseCheck(data);
function databaseCheck(userInput){
var mysql = require('mysql');
var connection = mysql.createConnection({
host : '12.34.56.78',
user : 'user',
password : 'password',
database : 'db'
});
connection.connect();
var query = connection.query('SELECT * from `table` WHERE `email` = userInput', function(err, rows, fields) {
if (!err) console.log(rows[0].username);
else console.log("connection failure");
});
connection.end();
}
So when ever I use this code, it keeps printing "connection failure" to the screen. It doesn't happen when I replace userInput with the "example#email.com" so I'm guessing there is some problem with using the variable userInput in the query. Can someone tell me what is wrong with my code?
Not only do you need to pass the userInput by appending it to the string, you need to escape it so that the query recognizes it as a string:
connection.connect();
var query = 'SELECT * from `table` WHERE `email` = ' + JSON.stringify(userInput);
console.log(query);
connection.query(query, function(err, rows, fields) {
if (!err) console.log(rows[0].username);
else console.log(err.name, err.message);
});
connection.end();
It also helps to make the error message more informative by displaying the actual error instead of a generic message.
Lastly, put connection.end(); inside the callback. According to what you said, it appears to work like you had it but it's generally a bad idea to end a connection before an asynchronous process using the connection has called back.
Ignore my last comment, it appears I was wrong in this particular case. According to the repository documentation, it says:
Closing the connection is done using end() which makes sure all remaining queries are executed before sending a quit packet to the mysql server.
Try this for testing and resolution. Printing to the log will let you see what you are putting in the query.
var querystring = "SELECT * from table WHERE email LIKE " +
userInput;
console.log(querystring);
var query = connection.query(querystring, function(err, rows, fields) {...