SQL injection in stored procedures and JSON - mysql

I am working on an API in Node/JavaScript with a MySQL database. I think this is a question not related to Node/JavaScript, just SQL, but I'm not sure about that.
Here I have a minimal example. Lets say I have a stored procedure to create an user:
DROP PROCEDURE IF EXISTS users_create;
CREATE PROCEDURE users_create(
IN user JSON
)
BEGIN
-- Retrieve values from JSON
SET #name = JSON_EXTRACT(user, '$.name');
SET #email = JSON_EXTRACT(user, '$.email');
SET #password = JSON_EXTRACT(user, '$.password');
SET #uuid = uuid();
-- Insert new user
INSERT INTO user (`id`, `name`, `email`, `password`) VALUES (
#uuid,
JSON_UNQUOTE(#name),
JSON_UNQUOTE(#email),
JSON_UNQUOTE(#password)
);
-- Retrieve created user
SELECT `id`, `name`, `email`, `status`, `createdAt` FROM user
WHERE id = #uuid;
END
Now I have a new user object —Javascript—:
const newUser = {
name: "This is me",
email: "me#example.com",
password: "strong_password",
}
And I call the procedure:
const createUserQuery = `CALL users_create('${JSON.stringify(newUser)}')`;
My question is: given that I'm using JSON to pass the data, and I'm using JSON_EXTRACT and JSON_UNQUOTE, am I prone to SQL injection here?

No, you are not prone to SQL injection. You might have other problems with the code. And your code might be unsafe if -- say -- the password is not encrypted.
However, the only SQL code that will run is the SQL in the stored procedure.
SQL injection is associated with dynamic SQL. That is SQL that is constructed from a string. A SQL injection account alters ("enhances"?) the string to do dangerous things. Your code is just assigning values in an insert. These might be wrong, but they will not cause any other statements to run.

Related

Node.js sequelize calling a stored procedure with input and output parameters

Database Query :
CREATE DEFINER=`root`#`localhost` PROCEDURE `signinVendor`(in inputvalue varchar(100), in pword varchar(30), **out retval tinyint**)
BEGIN
Node.js Code :
const spquery = await db.sequelize.query('call signinVendor(:inputvalue,:pword,**#retval**);',{
replacements:{
inputvalue : req.body.inputvalue,
pword : req.body.password
},
type: QueryTypes.SELECT,
raw: true
});
I am able to call procedures by entering the input value and password. But, I am unable to find syntax to access the return value through my sequelize nodejs code. How do we write code for out parameter in sequelize?
Okay it seems that we can pre write code and send output via select in database and avoid using output parameter. It seems Sequelize ORM has not yet implemented any way to accesss the output parameter through NodeJS.
FOR EXAMPLE:
IN SQL,
instead of creating an out parameter in stored procedure, you can use parameters to store your desired output.
set query = (select count(*) from table_name);
Then you can perform select without parameter like :
select query ;
This way you don't need to rely on output parameter in Sequelize ORM. You can simply select your desired output as the outcome of the stored procedure.

How to INSERT dynamic info in MySQL table using Node/Express JS

I am working on a react app with a Node/Express JS back end.
I am trying to insert a value, based on the input of the user, into MySQL database. Here is my Node post request:
// To create a new user account
app.post('/createUser', function(req, res) {
console.log('in createUser');
const {firstName, lastName, username, passcode, email} = req.query;
const CreateUser = `CALL CreateUser('${firstName}', '${lastName}',
'${username}', '${passcode}', '${email}')`;
mysqlConnection.query(CreateUser, (error, result) => {
if(error) {
res.status(500)
res.send("Could not create user account.")
} else {
console.log('create user cart');
console.log(`${req.query.username}`);
const CreateUserCart = `CALL CreateUserCart('${req.query.username}')`;
mysqlConnection.query(CreateUserCart, (error1, result1) => {
if(error1) {
res.status(500)
res.send("Could not create user account.")
} else {
res.status(201)
res.send("Account successfully created!")
}
})
}
})
});
The first stored procedure, CreateUser, is working fine. What I want to do is take the value in req.query of username and pass it to the stored procedure called CreateUserCart. Here is that stored procedure:
CREATE DEFINER=`lsharon`#`%` PROCEDURE `CreateUserCart`(IN
username VARCHAR(45)
)
BEGIN
INSERT INTO SHOPPING_CART(CustomerID)
SELECT CustomerID
FROM CUSTOMER
WHERE Username = username;
END
My desire is to insert the CustomerID that belongs to the user I just created into the shopping cart table. However, currently it is inserting every CustomerID into that table, even if it already exists there. I only want to insert the ID for the single user who just created an account.
Any suggestions would be appreciated!!
The query in your sp says
WHERE Username = username;
Your column names are case insensitive. So Username = username matches every row in your CUSTOMER. It's a version of WHERE 1 = 1 -- always true.
Try changing the name of your parameter, something like this:
CREATE PROCEDURE `CreateUserCart` (IN newUsername VARCHAR(45) )
BEGIN
INSERT INTO SHOPPING_CART(CustomerID)
SELECT CustomerID
FROM CUSTOMER
WHERE Username = newUsername;
END
MySQL stored programs are tons of fun to troubleshoot, eh? Lots of people avoid them for that reason. On the other hand, many large enterprises use lots of stored programs to interact with their data. But those enterprises usually pay the rather large licensing fees for Oracle or SQL Server. Those databases have better stored programming languages and tools. So, if you work at one of those places, you'll appreciate having a little bit of exposure to stored programs.
Pro tip there's a much more robust way to handle this workflow. Get your first stored procedure to end with something like
SELECT LAST_INSERT_ID() CustomerID;
This will make the procedure return a resultset, which you can handle just as if it were a SELECT query. LAST_INSERT_ID() gets the autoincrement value from the most recent INSERT.
Then read that customer id from your resultset, and pass it as a parameter to CreateUserCart rather than the customer's name.

Commands out of sync MySql PyQt5 using temporary tables

The main program is based on PyQt5 client and stores information on a MySql DB. The issue is that I need to import data from an MS Access DB (done by a QSqlQuery using the QODBC) load in a query (qryImport). I set a temporary table using a new connection (self.tempDb) to transfer the information to MySQL and I'm running a store procedure on a while loop to insert the data into MySql one record at a time. The problem is that we get the infamous "commands out of sync..."error when running the second while cycle, or in debug mode (PyCharm) as soon as I speed up the process. Could not find an answer to the problem over the net but the "Commands out of sync" form of the MySql Reference Manual https://dev.mysql.com/doc/refman/8.0/en/commands-out-of-sync.html that in my case was not very helpful.
I also try to close the connection and open the connection and clear the query after executing each cycle which takes care of the out of sync issue, but deletes the temporary tables on the connection in both cases besides not being very efficient.
Most of the information I found about it is referring either to PHP or python's MSQLDB, so I was not able to implement the manual´s recommendation of using msql_free_result() or msql_store_result() since I couldn't figure out how to do it.
I would appreciate any advise addressing this issue.
In the mean time I will try to use a permanent table as a workaround - that I don't like - or to export the query from MS Access to MySql eventually.
# ............................................
qryImport.seek(-1)
qry = QSqlQuery(self.tempDb)
qry.prepare("CALL importhorses_loaddata(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
while qryImport.next():
qry.addBindValue(QVariant(qryImport.value(0))) # accessid
qry.addBindValue(QVariant(qryImport.value(1))) # name
qry.addBindValue(QVariant(qryImport.value(2))) #rp
qry.addBindValue(QVariant(qryImport.value(3).toString("yyyy-MM-dd"))) #BirthDate
qry.addBindValue(QVariant(qryImport.value(4))) #sexid
qry.addBindValue(QVariant(qryImport.value(5))) #coatid
qry.addBindValue(QVariant(qryImport.value(6))) #isbreakable
qry.addBindValue(QVariant(qryImport.value(7))) #isBroke
qry.addBindValue(QVariant(qryImport.value(8))) #isPlayer
qry.addBindValue(QVariant(qryImport.value(9))) #Father
qry.addBindValue(QVariant(qryImport.value(10))) #Mother
qry.addBindValue(QVariant(qryImport.value(11))) #UbicacionID
qry.addBindValue(QVariant(self.importDate.date().toString("yyyy-MM-dd"))) #ImportDate
qry.exec()
if qry.lastError().type() != 0:
raise DataError("importHorses", qry.lastError().text())
if qry.first():
print(qry.value(0))
# .....................................```
Stored Procedure:
CREATE PROCEDURE importhorses_loaddate(IN p_accessid INT,........., IN p_inputdate DATE)
BEGIN
DECLARE code CHAR(5) DEFAULT '00000';
DECLARE msg TEXT;
DECLARE _horsebaseid INT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
code = RETURNED_SQLSTATE, msg = MESSAGE_TEXT;
SELECT code, msg;
ROLLBACK;
END;
START TRANSACTION;
SELECT horsebaseid FROM horses WHERE horsebaseid = p:accessid INTO _horsebaseid;
IF _horsebaseid != p_accessid THEN
INSERT INTO importedhorses(horsebaseid, ..........., inputdate)
VALUES(p_accessid, ......................, p_inputdate);
END IF;
COMMIT;
END

I want to prevent specific value insert into ulog2_ct table

I'm using ulog2 with mysql for gathering nf_conntrack values to DB in my bridge machine.
All things are works well, but I want to prevent to insert specific value which has "right(hex(orig_ip_saddr),8)='7f000001'".
I just execute query as "delete from ulog2_ct where right(hex(orig_ip_saddr),8)='7f000001' " periodically but its not good solution.
ulog2 provide queries to make and insert into the specific DB as https://github.com/inliniac/ulogd2/blob/master/doc/mysql-ulogd2-flat.sql
But I can't fully understand because I'm not sql expert.
I think some additional query would resolve what I want but I don't know how.
Insert query of mysql-ulogd2-flat.sql is as below: (line 435)
BEGIN
INSERT INTO ulog2_ct (oob_family, orig_ip_saddr, orig_ip_daddr, orig_ip_protocol,
orig_l4_sport, orig_l4_dport, orig_bytes, orig_packets,
reply_ip_saddr, reply_ip_daddr, reply_ip_protocol,
reply_l4_sport, reply_l4_dport, reply_bytes, reply_packets,
icmp_code, icmp_type, ct_mark,
flow_start_sec, flow_start_usec,
flow_end_sec, flow_end_usec)
VALUES (_oob_family, _orig_ip_saddr, _orig_ip_daddr, _orig_ip_protocol,
_orig_l4_sport, _orig_l4_dport, _orig_bytes, _orig_packets,
_reply_ip_saddr, _reply_ip_daddr, _reply_ip_protocol,
_reply_l4_sport, _reply_l4_dport, _reply_bytes, _reply_packets,
_icmp_code, _icmp_type, _ct_mark,
_flow_start_sec, _flow_start_usec,
_flow_end_sec, _flow_end_usec);
RETURN LAST_INSERT_ID();
END
Seo.
Just create a trigger on ulog2_ct
(check for syntax validation , as I have just typed it here)
CREATE TRIGGER trig_ulog2_ct
BEFORE INSERT ON ulog2_ct
FOR EACH ROW
BEGIN
DECLARE msg VARCHAR(255);
IF (right(hex(new.orig_ip_saddr),8)='7f000001') THEN
SIGNAL 'your error message Not to insert'
END IF;
END
Let us know

MYSQL Error 1414 - Error doesnt make sense

This is my stored procedure, which works fine on my local machine, but on PRODUCTION, it gives me the above error: Its complaining about the OUT parameter, the last one:
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `insert_projects`(
IN userName varchar(20),
IN projectName text,
IN projectDescription text,
IN projectCurrency text,
IN projectBudget1 double,
IN projectBudget2 double,
IN projectEndDate DATETIME,
IN expectedDelivDate DATETIME,
IN projectMessageForBidder text,
OUT projectCode bigint)
BEGIN
DECLARE
l_project_code bigint;
insert into projects
(PROJECT_CODE,
SR_USER_NAME ,
PROJECT_NAME,
PROJECT_DESCRIPTION,
PROJECT_CURRENCY,
PROJECT_BUDGET1 ,
PROJECT_BUDGET2 ,
PROJECT_STATUS,
PROJECT_END_DATE,
PROJECT_PAID,
EXPECTED_DELIV_DATE,
PROJECT_MESSAGE_FOR_BIDDER,
PROJECT_CREATION_DATE,
PROJECT_UPDATE_DATE)
values
(
0,
userName,
projectName,
projectDescription,
projectCurrency,
projectBudget1,
projectBudget2,
'Active',
projectEndDate,
null,
expectedDelivDate,
projectMessageForBidder,
NOW(),
NOW());
SELECT LAST_INSERT_ID() into l_project_code;
SET projectCode = l_project_code;
END
Below is my C# code, that sets up the parameters :
MySqlCommand command = new MySqlCommand("insert_projects", dbcontroller.conn);
command.CommandType = System.Data.CommandType.StoredProcedure;
// Add parameters for the insert_projects STORED PROC
command.Parameters.Add(new MySqlParameter("userName", SessionBag.Current.UserName));
command.Parameters["#userName"].Direction = System.Data.ParameterDirection.Input;
command.Parameters.Add(new MySqlParameter("projectCode", MySqlDbType.Int64));
command.Parameters["#projectCode"].Direction = System.Data.ParameterDirection.Output;
command.Parameters.Add(new MySqlParameter("projectName", model.projectName));
command.Parameters.Add(new MySqlParameter("projectDescription", model.projectDescription));
command.Parameters.Add(new MySqlParameter("projectCurrency", model.projectCurrency));
command.Parameters.Add(new MySqlParameter("projectBudget1", model.projectBudget1));
command.Parameters.Add(new MySqlParameter("projectBudget2", model.projectBudget2));
command.Parameters.Add(new MySqlParameter("projectEndDate", model.projectEndDate));
command.Parameters.Add(new MySqlParameter("expectedDelivDate", model.expectedDelivDate));
command.Parameters.Add(new MySqlParameter("projectMessageForBidder", model.projectMessageForBidder));
try
{
command.ExecuteNonQuery();
TempData["projectCode"] = (Int64)command.Parameters["?projectCode"].Value;
TempData["frompage"] = "AddProject";
dbcontroller.conn.Close();
return RedirectToAction("MyProjectsHeaderSR", "Projects");
}
Why is is complaining about this? My local machine works fine. The table "projects" contains a field called project_code which is set to BIGINT auto-increment... The rest of the fields are normal datatypes.
Ok, I have created a variable .. and passed it in .. same error
if (dbcontroller.DBConnection())
{
Int64 projectCode = 0;
MySqlCommand command = new MySqlCommand("insert_projects", dbcontroller.conn);
command.CommandType = System.Data.CommandType.StoredProcedure;
// Add parameters for the insert_projects STORED PROC
command.Parameters.Add(new MySqlParameter("userName", SessionBag.Current.UserName));
command.Parameters["#userName"].Direction = System.Data.ParameterDirection.Input;
command.Parameters.Add(new MySqlParameter("projectCode", projectCode));
command.Parameters["#projectCode"].Direction = System.Data.ParameterDirection.Output;
It still doesnt work
The error you describe is the result of a bug in older versions of MySQL, resolved in MySQL 5.5.3. It's probable that your local machine is running a version of MySQL where the bug has been fixed, and the production server is running an older version.
Although the best solution may be to upgrade the server, as a workaround until then, you could change the procedure to simply select the LAST_INSERT_ID() (not into a variable, but into a result set), and process that result into your variable.
Incidentally, a similar situation may arise when using INOUT parameters. Although you are using an OUT parameter (and so the following does not apply in your case), the situation would arise when an INOUT parameter but not providing a variable to populate, the following would similarly apply.
The line:
command.Parameters.Add(new MySqlParameter("projectCode", MySqlDbType.Int64));
...is providing a named parameter to the command object, and a type, but not giving it anything to populate. If you were using an INOUT parameter, you would need to provide a empty variable and pass it in; it'll give the SP something to output to.