This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 1 year ago.
I have a Single Page Application in which the browser does all the logic work. Except for initial loading, the server is pretty much a fancy interface to the database.
The browser sends data dictionary keys, column name / value pairs, and where clauses for SELECT, for example. The server assembles the parts into SQL, executes the queries, and replies. NEW: In a SELECT, for example, the table name and columns pulled are from the data dictionary - the browser supplies the data dictionary key and the SELECT where clause.
This very open environment is very susceptible to SQL Injection attacks. The goal is to prevent damage from said attacks.
Problems to be Overcome
First, as discussed, it is impossible to paramaterize a random SELECT where clause - SELECT cannot use a prepared statements.
Second, mysqli, the library for paramaterized statements with MySQL, does not support NULL nor MySQL functions, eg, CURRENT_DATE or NOW(), as discussed.
Proposed Solution
First, if SELECT cannot be paramterized, then execute SELECT by a user who has no DML or DDL rights. This will prevent SQL Injection Attacks from changing the database.
Second, write a wrapper function for mysqli that will allow NULL and MySQL functions to be passed as parameters. This will allow parameters to be easily used for all DML.
Third, shadow highly sensitive data where it cannot be seen or touched by normal queries or normal users. This will put sensitive data, such as passwords, out of the range of attacks.
Forth, write a wrapper order to enforce the user / query type relationship. This will ensure SELECT are executed by the select user, for example
The result of that effort is here. The question is, logically, will this approach successfully protect against SQL Injection Attacks?
Non-Answer Answers
I proposed this same question before. Due to my poor job of presentation, received answers and comments ended up focusing on spurious issues instead of addressing the (admittedly) difficult question - does this actually work?
As a reference, here are some of those comments and answers.
Use PDO's prepared statements
First, prepared statements cannot be used for all SELECTs - how does PDO help? Second, mysqli does not accept NULL or MySQL functions - how does PDO help?
Why re-invent the wheel
If you know of an approach that overcomes these problems, I would really really like to know - this is a difficult problem.
no mysql_real_escape_string() in sight
Values should be sanitized before being passed to the database query functions. mysql_real_escape_string() is one of a set of sanitizing functions available, for example, one would use a different sanitizer for dates.
too much work
Please share with me your know of any approach that overcomes these problems - I would really like better insight. That said, from my setting up the whole thing again, following my notes it took between 30 and 45 minutes. No time costs incurred thereafter.
I am happy with mysqli
How are you going to prevent SQL Injection Attacks when you cannot parameterize your SELECT? Do you expect to never use NULL when updating a column? Every man to his own poison, but these are issues I hope to solve - not live with.
#Konerack pointing out limited number of parameters
Right. Changed to code to use eval() (shudder) which solved the problem. Needs security review.
The Question Again
Will this approach protect against SQL Injection Attacks while overcoming the problems of no parameterized SELECT and mysqli's parameter limitations?
The answer is simple:
For parameters use PDO or mysql_real_escape_string().
For other dynamic SQL (table names, column names, syntax elements) use whitelisting.
See: How to prevent SQL injection with dynamic tablenames?
You cannot trust the browser to keep within the bounds you set in Javascript. This can be easily manipulated. So you must treat all data from that end as untrusted. Make sure you check all elements in the where clause against a list of allowed keywords.
For this purpose you'd use:
Check against whitelist for symbols: =, <>, >, LIKE, NULL, IS.
Check against a whitelist for boolean operators: AND, OR, XOR, NOT
All values must be properly enclosed in single ' quotes and you will need to feed these through mysql_real_escape_string() to make sure there are no shenanigans.
All values not in (1,2) and not enclosed in single quotes are column names, check them against a whitelist of allowed column names.
Reject all other input.
Related
Does execute($input_parameter) protect from sql injections just like bindParam/bindValue?
If the answer is yes, bindParam()/bindValue()/execute() are invulnerable to any sql-inject attack? Or I need to take measures to prevent such attacks?.
Thanks for help!.
As far as execute($input_parameters) being as safe as separate bindParam/bindValue/execute steps, the answer would appear to be basically, yes.
However, you might still need to take further measures depending on how you constructed the query string that you pass to your PDO::prepare call. It is not always possible to parameter-ize everything in the prepared query string. For example, you can't use a parameter for a table or column name. If you allow user data or any external data into that query string you must still sanitize that data before passing the string to prepare.
Refer to these stackoverflow questions for more details:
how safe are PDO prepared statements
Are PDO prepared statements sufficient to prevent SQL injection?
In general you should be filtering all input data anyway, so if you wanted to be extra safe you could sanitize any input data that is destined for SQL-type stuff using the filters appropriate for your needs, or even writing a FILTER_CALLBACK custom function if you wish.
In the case of table or column names coming from user-provided data, a common validation technique is to check the values against arrays of allowable names.
Hope this helps. Good luck. Stay safe! ;)
Yes, it does the same thing. I cannot say that it is invulnerable, because the underlying SQL engine could itself be vulnerable. But that really isn't in your hands anymore.
So for all practical reasons, yes, its safe.
EDIT: Look at the PHP Documentation (1st and second example). One is with bindParam() and the other uses execute().
I realized that the following part of my application has potential for MySQL injection.
$id= $_GET['id'];
$query = "SELECT * FROM clients WHERE id = $id";
//run query
Is it enough security for me to check is_numeric($id) before running the MySQL query? Or is it necessary for me to re-write my code using prepared statements?
You SHOULD use prepared statements as mysql_* functions are deprecated. That being said, a numeric value would make that SQL statement definitively safe as SQL injections require the variable to actually have SQL like statements in them.
An example would be 1;-
It will reduce the attack surface if you restrict the parameters to just an integer but you also want to validate the parameter on the server side (don't leave the security checks on the client-side since they client can bypass those very easily).
For application security (and especially PHP security since it is riddled with security headaches) it is always a good idea to consider that all user-supplied data is malicious. So double check that all parameters fit into the criteria expected before allowing the application to act on that data and process it.
You will do yourself a favor by using PHP prepared SQL statements as those are an added mechanism (on top of your own validation) to significantly reduce the injection attack surface.
Here is a resources to get you more familiar with PHP prepared statements:
http://php.net/manual/en/mysqli.quickstart.prepared-statements.php
The idea is that it avoids injection attacks by specifying what kind of datatype the parameter is and builds a safer query string for you. But again - always inspect incoming/user-supplied data before processing it further.
If you don't have time to rewrite your queries using prepared statements, I would say I'd have more confidence in casting the user input.
$id = (int) $_GET['id'];
Since this variable is now an int, there is no way it can contain malicious input. Of course, you should still do any necessary range validation on it (e.g. if negative numbers should be disallowed).
I've assumed this column is an integer, but (float) can be used in the same way here, for data that is numeric but not integer.
For the avoidance of doubt, parameter binding is still the best approach to injecting user input into your queries. My answer here is intended to answer the thrust of the question directly i.e. is there a way to make queries safe without binding? The answer above shows that the answer is yes.
This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 8 years ago.
First of all, let's get one thing straight, while im sure it's not the ONLY solution to sql injections, PREPARED STATEMENTS ARE BETTER and MORE SECURE... that's granted.
So let's not debate it anymore, as I just needed to explore the escaping options and target the user inputs rather than the statement..
(a bit of back story)
I have a huge application w/ lots of basic SQL statements which i need to make "safer" in a short period of time. Converting all statements to PDO is a monster of a task which requires resources not available to this situation.
But good thing is that there's a hook in the application where ALL incoming $_GET/$_POST passes through a function before making available as plain variable in the scripts... ( eg.globalize($_POST) )
So i'm looking to target that function instead and focus on "cleaning" user input ...
I pretty much got a good grasp of SQL injection techniques, and basically much of its concept, such as:
1) Whenever there is user input involved in a statement, that's when the attacker has an opportunity
2) The main mission is to break your statement's quoting by simple single or double quotes as string, or character codes, in the attacker's input..
3) Escaping is good at preventing SQL injections except for that situation when the DATABASE character encoding is able to ready multibyte representations of those nasty illegal strings and the script based escaping misses it.
Based on the above premise, what if:
1) All my SQL Statements follow a standard where user input is quoted using single quotes ALWAYS, therefore there is no guess work for me anymore on what type of quote i need to escape (single quotes)
2) all user inputs (POST / GET) go through a function (eg. globalize() ) that basically addslashes() and globalizes each variable
3) This is an intranet application and the scripts can be made aware of the database encoding support.
QUESTION:
What are the threats currently available in the above situation and how can we handle them?
...i personally was looking into simply adding routines to the globalize() function similar to mysql_real_escape_string() .. or go as far as "tweaking" the SQL statements to use mysql_real_escape_string() as that is a lot easier/faster to do than programming statements on each sql query..
PS
Of course this script may never be as secure as prepared statements, but i just need to make it hard enough for most attackers (not looking to 100% bullet proof it as it is not that worth it)
Using
mysql_real_escape_string($link,$non_escaped_string);
See here: http://php.net/manual/en/mysqli.real-escape-string.php
While it might well work, there are a few possible issues.
If you use addslashes then there are potential exploits, hence for MySQL mysql_real_escape_string() is generally better. There is a potential flaw in mysql_real_escape_string() (which also applies to the default use of pdo), but this is so esoteric you pretty much have to try to make your code open to an attack based on it.
The bigger issue is that your user input could possibly be used on different databases with different character sets. Hence you should specify the link to the database that the escaped string will be used in. If you do not specify this the the details of the last link opened will be used. If no link has already been used then it will try and connect itself with some default connection parameters and the results are likely to be unpredictable.
If you just escape all the $_POST / $_GET / $_REQUEST arrays (and remember that $_COOKIES array is also subject to being changed by a mischievous user) then the likely place you would do this would be when the scripts are first loaded, when you are unlikely to have connected to the appropriate database.
A further issue is that some of the code you are dealing with (and which you seem not to have the time deal with in detail) is bound to already escape the input data. Hence you will land up double escaping it with strange results. Related (but less likely to be an issue) is escaping numeric fields
Does execute($input_parameter) protect from sql injections just like bindParam/bindValue?
If the answer is yes, bindParam()/bindValue()/execute() are invulnerable to any sql-inject attack? Or I need to take measures to prevent such attacks?.
Thanks for help!.
As far as execute($input_parameters) being as safe as separate bindParam/bindValue/execute steps, the answer would appear to be basically, yes.
However, you might still need to take further measures depending on how you constructed the query string that you pass to your PDO::prepare call. It is not always possible to parameter-ize everything in the prepared query string. For example, you can't use a parameter for a table or column name. If you allow user data or any external data into that query string you must still sanitize that data before passing the string to prepare.
Refer to these stackoverflow questions for more details:
how safe are PDO prepared statements
Are PDO prepared statements sufficient to prevent SQL injection?
In general you should be filtering all input data anyway, so if you wanted to be extra safe you could sanitize any input data that is destined for SQL-type stuff using the filters appropriate for your needs, or even writing a FILTER_CALLBACK custom function if you wish.
In the case of table or column names coming from user-provided data, a common validation technique is to check the values against arrays of allowable names.
Hope this helps. Good luck. Stay safe! ;)
Yes, it does the same thing. I cannot say that it is invulnerable, because the underlying SQL engine could itself be vulnerable. But that really isn't in your hands anymore.
So for all practical reasons, yes, its safe.
EDIT: Look at the PHP Documentation (1st and second example). One is with bindParam() and the other uses execute().
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to prevent SQL injection in PHP?
I am working for a video streaming website for my college library. I am using PHP and MySql. I have not used any parameterized queries in this project.
Recently I came to know about SQL injections. Now that my code is almost done and I have to submit the project in the next two days, how can I now ensure that my code is not SQL injection prone?
Converting the whole thing in to a parameterized interface is what I can't do now. What should I do now to avoid SQL Injections on my website?
The basic idea to prevent SQL injections (if not using Prepared Statements) is to escape your data.
When you inject some expected integer value into an SQL query, make sure it's an integer, using intval().
When you have a decimal/numeric field in your table, use floatval().
And when you have a string (char, varchar, text) field in your table, use the function provided by your API to escape strings :
mysql_real_escape_string()
mysqli_real_escape_string()
PDO::quote()
I really recommend that you go back and do it right with parameterized queries. It is the only solid path towards security. It likely won't take too long to do this once you get started.
You should also know that websites are never "finished". When you launch a site, your work has just begun. Fixing security troubles as you learn about them is part of it, and this is no different.
You'll want to make sure any user provided inputs that get used in SQL queries are escaped using the PHP function mysql_real_escape_string and if you are letting people submit text to run htmlentities on the provided text so XXS isn't possible. If possible, white-list user provided input and discard anything else
This is just touching the surface of what you can do but look into query escaping and preventing cross site scripting.
Use PDO (or alternatively mysqli or some abstraction layer) and prepared statements.
Quick example:
$pdo = new PDO($dsn);
$stmt = $pdo->prepare("SELECT name FROM users WHERE id = ?");
$stmt->execute(array($unsafe_id));
$name = $stmt->fetchColumn();
In this example, $unsafe_id will be safe to use. To quote the manual page:
Calling PDO::prepare() and
PDOStatement::execute() for statements
that will be issued multiple times
with different parameter values
optimizes the performance of your
application by allowing the driver to
negotiate client and/or server side
caching of the query plan and meta
information, and helps to prevent SQL
injection attacks by eliminating the
need to manually quote the parameters.
PDO will emulate prepared
statements/bound parameters for
drivers that do not natively support
them, and can also rewrite named or
question mark style parameter markers
to something more appropriate, if the
driver supports one style but not the
other.