How can we prevent SQL-Injection from MySQL? - mysql

We all know that we can prevent SQL-Injection in PHP by Prepared Statement of query or mysqli_real_escape_string() function. But if i want to prevent it from MySQL side ? Do you have any idea? How can i achieve it ?

You can use stored procedure to query the database. The stored procedure checks the data type and parameters supplied, if there is a mismatch a query is not executed.
Here is a sample of stored procedure you can use to insert a record in mysql -
DELIMITER $$
CREATE PROCEDURE book_Insert (
in title varchar(30),
in isbn varchar(30),
out bookID tinyint(3) unsigned
)
BEGIN
insert into books (title, isbn)
values(title, isbn);
set bookID =last_insert_id();
END $$

As the comment from #Ferrybig says, on the MySQL side there's no way to be sure that an SQL query is legitimate or the result of SQL injection.
Suppose the database server receives this query:
SELECT col1, col2, col3 FROM MyTable WHERE account_id = 1
UNION SELECT user, password, NULL FROM mysql.user
This looks pretty suspicious. Why would the app want to read all passwords, and append it to the query we expect to see? There's a strong chance this is an attempt at hacking.
Another example:
SELECT col1, col2, col3 FROM MyTable WHERE account_id = 1
OR account_id = 473
Is this legitimate? Or is it illicit? Is the query executed for a user who should have privileges to read data for account 473? How can you know? On the MySQL side, the query looks exactly the same whether it was the result of SQL injection or not.
It might have been code like the following PHP, which is vulnerable to SQL injection (this is not a failing of PHP, because similar vulnerable code can be written in any programming language):
$sql = "SELECT col1, col2, col3 FROM MyTable WHERE account_id = " . $_GET['id'];
If the attacker caused the input parameter to be: "1 OR account_id = 473"
The point is, once the query is formatted in the app and submitted to MySQL, MySQL can't tell how it was formatted. MySQL can only trust that the developer of the code did format the query in a safe way.
One method of blocking illicit queries is by using a type of Web Application Firewall (WAF) that you design to recognize legitimate inputs and block requests that are not legitimate. In other words, you need to program the WAF with a whitelist or set of patterns to recognize safe requests. This list will be unique for each app, so you need to be very familiar with the app. Any modification to the app may require you to update the WAF's whitelist.
The WAF is typically a proxy at the http layer, to prevent illicit request from reaching the web app. There are also proxy solutions to protect the request between the app and the database. You program the proxy to recognize which SQL queries are expected, and it blocks any queries that have unexpected terms. It would
An example of a database firewall is https://www.oracle.com/database/technologies/security/audit-vault-firewall.html
They aren't perfect. They may block queries you want to allow. And they don't work for dynamic queries run in stored procedures.
Ultimately, you should just establish safe programming standards in your application code. Use query parameters whenever you combine unsafe data with your SQL queries.

You can use
assuming that you input a parameter $bookID
DELIMITER $$
CREATE PROCEDURE `spGetBook`(
in bookID int)
BEGIN
SELECT *
FROM categories
where bookID=bookID;
END $$

Related

what sql injection can do to harm you

The following is a textbook sql injection example:
SELECT id FROM table WHERE username = '$username' AND password = '$password'
if your site doesn't protect against sql injection you can simply send in password = anything' OR 'x'='x as the input and login without a password. easy.
attacker can also pass in '; DROP TABLE table; to drop the table from the db. And of course if the sql connection does have DROP permission than it will not work. Also attackers probably want to get more benefits by doing something other than simply dropping your table.
So the question is can the attackers carry out attacks to do UPDATE on table, get the structure on all tables, list tables or db by only attacking this vulnerability?
p.s: not that I want to use it to attack people but I am kinda curious what could happen at worst on my db..
Potentially, sure. If you can inject a DROP TABLE table; into the SQL statement that is executed, you could just as easily inject an UPDATE statement that modified whatever rows of whatever tables you'd like. You can also frequently add or modify a SELECT statement to show you information that you're interested in. For example, if you have a query like
select name
from people
where person_id = '$person'
you could inject something like
anything` union all select table_name from information_schema.tables
to produce a statement like
select name
from people
where person_id = 'anything'
union all
select table_name
from information_schema.tables
to show you all the tables. You can do the same sort of thing to get a list of columns in the tables and then start running queries to see what data is in the various tables.

MySQL "INSERT" and SQL injection

I have this simple mysql query:
INSERT INTO table (col1, col2) VALUES ('1', '2')
col1 and col2 are foreign keys for another table so any value for col1 and col2 must be present in the other table or otherwise the row won't be inserted.
Is there still any risk of SQL injection in this case? If i receive these col values from PHP POST, do I still need to bind them before insertion into the database or they are already secure as the cols are foreign keys?
Yes. All input from users needs to be check for sanitized. E.g. if a user is sending you a string like that '2'); drop table <table> as your second value it might get executed and giving you some surprise. (String might not work exactly, but I think you got the point)
It's indeed prone to SQL Injection, since the user could, for example, break your query and get information about your RDBMS and your database schema in the error message, and use it to prepare another attacks to your applications.
There are a lot of ways to explore SQL Injection issue.
Yes, there is always a risk of injection, even with foreign key constraints. If I know what is a valid col1 and col2 value, I can use those values to construct an attack. It is best to always scrub user input and assume the user is trying to hurt your database.
When constructing database queries in PHP, use an interface that allows you to use placeholders for your data that will handle any escaping automatically. For example, your query would look like:
INSERT INTO table (col1, col2) VALUES (:col1, :col2)
Then you can bind against that using the appropriate method in a driver like PDO. If you're disciplined about using placeholders for user data, the chance of a SQL injection bug occurring is very low.
There's a number of ways to properly escape user input, but you must be sure to use them on all user data, no exceptions. A single mistake can be sufficient to crack your site wide open.

SQL Injection DROP TABLE not working

I need to demonstrate SQL Inject using PHP/MySQL. I want to inject a DROP TABLE query in a login form but it never works. (TRUNCATE table works fine OTOH).
After I input '; drop table users; # as field input; query turns out to be
SELECT * FROM `users` WHERE `email` = ''; DROP TABLE users; #' AND `password` LIKE '3232';
But it never works using mysql_query() function. When I copy/paste this query in PHPmyAdmin directly, it works perfectly and table gets dropped. What can be the issue?
MULTIPLE SQL Execution is not enabled by defaults.
So SQL Injection like ;drop table won't work. please enable multiple sql execution. this could be enabled like http://php.net/manual/en/mysqli.quickstart.multiple-statement.php if you are using mysqli.
useful SQL Injection is :
SELECT COUNT(*) FROM users
WHERE user_id = '$user_id' AND passwd = '$passwd'
and user inserts passwd to ' || '1' = '1.
This is not possible in php/MySQL as php does not support stacked queries. The moment you inject semicolon (;) it will fail.
You can do many other much more creative exploits using sql injection in a php-mysql application though.
Enumerate all databases
Table Names and Column Names
Values stored in tables
Upload a php backdoor
Check Out for SQL-Map as well.

save stored procedure output into a table

I have execute only access to a stored procedure.
This SP seems to select some data from multiple tables, and returns one row. I need to store two columns of the output of this SP into a table.
Is there any way to do this within MySQL?
If it returns a row, this is a stored function and not a stored procedure. You can use something like the following to insert into your table:
INSERT INTO tablename SELECT (SELECT col1, col2 FROM (SELECT somefunction()))
Otherwise, it will be a stored procedure and you should do something like this, assuming that #var1 and #var2 are output parameters:
CALL someprocedure(#var1, #var2, #var3)
INSERT INTO tablename SELECT(#var1, #var2)
See the documentation about Create Procedure and Create Function for more information about functions versus procedures.
MySQL has an extension to stored procedures that allows the procedure to return one or more result sets to the client, as if the client had issued a SELECT query... but those results are ephemeral. They don't persist and they can't be stored in variables or otherwise accessed after the procedure finishes -- they can only be "fetched" the one time.
There is a way to make them accessible without breaking the way the procedure already works, as I discussed here, but you can't do it without a change to the procedure:
How to use Table output from stored MYSQL Procedure
The idea is for the procedure to write its output in a temporary table, and then return it to the caller by calling SELECT against the temporary table -- but to leave the temporary table behind so that the caller can access it directly if desired.
That's not exactly the same as what you're asking though, which is why I didn't mark this question as a duplicate, since you, unlike the other poster, do not appear to have administrative control of the procedure... but unless you can make the case for a change like this, there's not another way within MySQL to access those returned values, since they only exist in the result-set that's returned.
Of course, procedures do have optional OUT parameters, where you can hand variables to the procedure as part of arguments you use to call it, and it can set those variables, so that they'll have the values you need when the procedure is done, but that only works when the return values are scalars and would require a change to the procedure's interface, since procs in MySQL do not have "optional" arguments... if the procedure were changed to permit this, it would require an increased number of arguments to be provided every time it was called, and if other components are calling it, that could easily break other things.

How can I be DRY in columns names in this MySQL procedure?

I'm referencig name, description and user_id columns of meta table. Twice, and maybe more (who knows?) in future. Those columns are used to compute the ETag of my meta resource.
Adding one column that contributes to compute ETag in the future will force me to change the code N times, and this is bad.
Is there any way to make it DRY and store these column names elsewhere? Because I'd like to use these column names also when INSERT on meta is performed.
IF only = true THEN
-- Calculate ETag on meta fields only
UPDATE meta
SET etag = etag(CONCAT(name, description, user_id))
WHERE id = meta_id;
ELSE
-- Calculate Etag on meta fields and meta customers
BEGIN
DECLARE c_etags VARCHAR(32);
-- Compute c_etags
UPDATE meta
SET etag = etag(CONCAT(etag(name, description, user_id), c_etags))
WHERE id = meta_id;
END;
END IF;
Disclaimer: this code is untested, I'm pretty new to MySQL stuff, apart for simple statements.
EDIT: etag is MD5 MySQL function. Maybe this is one option:
CREATE PROCEDURE set_meta_etag(IN meta_id INT, IN related TEXT)
NOT DETERMINISTIC
BEGIN
UPDATE meta
SET etag = etag(CONCAT(name, description, user_id,
IF(related IS NOT NULL, related, '')))
WHERE id = meta_id;
END //
-- First call
CALL set_meta_etag(meta_id, NULL);
-- Second call
CALL set_meta_etag(meta_id, c_etags);
But it won't work for INSERT statement.
The obvious thing (foreach column, if it's the one I want, use it to help make the etag) doesn't work in SQL with any ease, because SQL doesn't, historically, contemplate column names stored in variables.
You could write a program in your favorite non-SQL programming language (Java, PHP, etc) to create and then define your procedure.
You could also use so-called "dynamic sql" to do this, if you were willing to do the work and take the slight performance hit. See
How To have Dynamic SQL in MySQL Stored Procedure
for information on how to PREPARE and EXECUTE statements in a stored procedure.
By the way, I have had good success building systems that have various kind of metadata stored in the column contents. For example, you could write code looking for the string '[etag]' in your column contents. The comments for columns are stored in
information_schema.COLUMNS.COLUMN_COMMENT
and are very easy to process when your program is starting up.
If you know this is confined to one table, you could add a trigger. Using an AFTER trigger should allow your stored proc to work for both INSERT and UPDATE. See MySQL Fire Trigger for both Insert and Update.