I am currently working on a NodeJS backend script that parses incoming HTTP requests to write to and read from a MySQL database for work. I tried to protect it against SQL injections by using a kind of two-layer protection.
To write to the database the user needs to provide valid JSON. This is how the JSON's keys need to be for the script to write to the DB. If they aren't, the request will be cancelled.
{
"uuid": "1234-5678-abcd-efgh",
"product_id": 6,
"product_extras": "color=red",
"product_count": 2,
"buyer_name": "X Y",
"shipping_address": "XY Street 5"
}
The first layer that checks this input is a very basic blacklist. Here the USER_INPUT
variable is the JSON above that has already gone through the validation process.
let BLACKLIST = ["DROP ", "DELETE ", "INSERT ", "UPDATE ", "SELECT ", "WHERE ", "ALTER "];
let dont_execute = false;
for(var i = 0; i < BLACKLIST.length; i++) {
if(JSON.stringify(USER_INPUT).toUpperCase().includes(BLACKLIST[i].toUpperCase())) {
console.log("\x1b[31mreceived blacklisted command - aborting");
dont_execute = true;
return false;
}
}
After that validation, if dont_execute
is still false, the second layer will be called and so the query will be escaped and sent just like this:
// setting.sqlconnection.table_name is equal to "orders" in this case. Also this variable can't be changed or specified by the user. It's pulled from a settings.json file
sql.sendQuery("INSERT INTO " + setting.sqlconnection.table_name + " (uuid, productid, orderid, productextras, productcount, buyername, shippingaddress) VALUES (" + sqlconnection.escape(uuid) + ", " + sqlconnection.escape(parseddata.product_id) + ", " + null + ", " + sqlconnection.escape(parseddata.product_extras) + ", " + sqlconnection.escape(parseddata.product_count) + ", " + sqlconnection.escape(parseddata.buyer_name) + ", " + sqlconnection.escape(parseddata.shipping_address) + ")");
I have tried many different injections like this one:
{
"uuid": "1234-5678-abcd-efgh",
"product_id": 6,
"product_extras": "color=red",
"product_count": 2,
"buyer_name": "X Y",
"shipping_address": "');DROP orders;--"
}
And just like I expected, it didn't work because first of all it was cought in the first blacklist layer. But after disabling it just to test things out the whole SQL query was just interpreted like a string as the apostrophe was escaped ('
became \'
), which is just what I wanted. The query that will be sent with the SQLI-infected JSON will then as far as I know look like this in the end:
INSERT INTO orders (guid, productid, orderid, productextras, productcount, buyername, shippingaddress) VALUES ("1234-5678-abcd-efgh", 6, null, "color=red", 2, "X Y", "\');DROP orders--")
But the problem is, after showing this to my boss he said it wouldn't be safe and it's still vulnerable to SQLI. So my question is if he is right and if so, what I could do to improve it.
Additional information:
I am using the npm package mysql
for the database connection
I am using XAMPP with MySQL to host the database locally
I'm sorry if I couldn't provide more information but I'm pretty sure that I am not allowed to post more stuff than this.