Lets say I were to perform a prepared statement like this:
$qry->prepare('UPDATE table_name SET column1 = ? string_column = ? WHERE column3 = ? AND column4 = ?');
$qry->bind_param('sbid', $string, $blob, $int, $double);
$int = 'non int value'; /* gives 0 in the database */
$blob = 'some string';
$string = 'another string';
$double = $double;
$qry->execute();
$qry->close();
Let's just say I only wanted to perform the query once, I just used the prepared statement in the name of security. From what I've been reading - its more overhead to use prepared queries only once, and that amounts to compromising performance for the security benefits. That being said - what would be the performance/security difference in doing the same query one time like this.
$int = (int) $int;
$blob = "'" .mysql_real_escape_string($blob) ."'";
$string = "'" .mysql_real_escape_string($blob) ."'";
$double = (double) $double;
$db->query("UPDATE SET column1 = $int, column2 = $blob WHERE column3 = $string AND column4 = $double ");
PS. I am not interested on how Prepared statements improve the performance but about the security and speed difference for a single query.
There is quite a lot to that. Some random points
Single use prepared statements do impose a (more than theoretical) performance penalty, which is higher, if a lot of connections exist to the MySQL server. (Think: Context switches)
But you should not run a DB server so close to its limits, that this makes the difference.
But you not always have the choice (Think: shared hosting)
or:
There are some (or even many) cases, where prepared statements do not offer a security benefit - there is a lot of business logic, where no user-generated data is involved (Think: Jointables, that only carry IDs) or where the user-generated data has to be validated beforehand for other reasons (Think: Price calculations, memcached lookups, ...)
But selecting one of many styles for each single query results in unmaintainable code.
But it is sometimes unavoidable (Think: There is no prepared query support for the IN ( ) construct)
often overlooked:
Prepared queries sometimes make it harder to be RDBMS-agnostic
But prepared queries offer the best know protection against SQL injection.
My favorite:
it is common advice to simply always use prepared queries
But the majority of living things on this planet would advise you to eat feces or rotting organic substance.
So the choice of style often has to be made on a case-by-case basis. We have adopted the way of encapsulating all DB access including parameter management in a standardized library, that is simply require()ed, so you can drop-in replace with prepared queries, escaping or whatever you want and your RDBMS supports.
Thank you for the great question.
As a matter of fact, you could use both methods at once.
Most people do confuse the idea of a prepared statement in general with [very limited] implementation offered by major DBMS. While the latter can be questioned, the former is indeed the only way.
Take a look at the example. Let's run your query using safeMysql:
$sql = "UPDATE SET column1 = ?i, column2 = ?s WHERE column3 = ?s AND column4 = ?s";
$db->query($sql, $string, $blob, $int, $double);
It performs the very string formatting like your code does, but does it internally. Why? Because it doesn't matter how it's implemented internally (by means of native prepared statement or manual formatting), but it is essential to use a prepared statement to assembly your query either way.
There are some essential points about prepared statements, overlooked by most people:
it makes formatting being always complete (Although in your example you're doing the right thing and make complete formatting, it is still very easy to slip into incomplete one, like this:
$colname = "`$colname`";
your formatting being always the right one. it won't let you do something like
$colname = "`" .mysql_real_escape_string($colname) ."`";
which would be useless and lead you to injection
it will make formatting obligatory. With assembling a query your current way it is very easy to overlook a variable or two.
it will do proper formatting as close to the query execution as possible. That's the point of great importance, as
it will not spoil your source variable (what if query failed and you want to echo it back?)
it won't let you to move formatting code somewhere away from the query, which may lead to fatal consequences.
after all, it will make your code dramatically shorter, without all that boring manual formatting!
That's the real benefits of a prepared statements, which guarantee the safety and thus made them so overly popular. While that thing with server-side preparation, although being quite smart, is just a particular implementation.
Also, taking the idea of a prepared statement as a guide, one can create a placeholder for the everything that may be added into query (an identifier or an array for example), making it real safe and convenient to use.
Keeping all the things in mind one have to implement the very idea of a prepared statement in their DB access library, to make the code safe and short.
Just a couple of examples from safeMysql:
$name = $db->getOne('SELECT name FROM table WHERE id = ?i',$_GET['id']);
$data = $db->getInd('id','SELECT * FROM ?n WHERE id IN ?a','table', array(1,2));
$data = $db->getAll("SELECT * FROM ?n WHERE mod=?s LIMIT ?i",$table,$mod,$limit);
$ids = $db->getCol("SELECT id FROM tags WHERE tagname = ?s",$tag);
$data = $db->getAll("SELECT * FROM table WHERE category IN (?a)",$ids);
$data = array('offers_in' => $in, 'offers_out' => $out);
$sql = "INSERT INTO stats SET pid=?i,dt=CURDATE(),?u ON DUPLICATE KEY UPDATE ?u";
$db->query($sql,$pid,$data,$data);
Just try the same with conventional mysql(i) and see the amount of code it takes you.
You may note that with usable prepared statements you have to mark them with type, because there are more types than just simple string, and it's the only reliable way to tell a driver how to format your variable.
I believe they are equally secure from security point of view, but using prepare does not only make your SQL secure, but also make you FEEL secure. You cannot trust yourself to manually escape and convert to proper type all the time. If you write 10,000 different SQL queries, you will tend to forget to escape one or two.
So in conclusion, prepare is a better habit to fight against SQL injection. Putting PHP variable directly to the SQL query make me feel uneasy when sleeping at night.
Related
I've watched Computerphile's video many times on this subject(for any of you who want, this is the link: https://www.youtube.com/watch?v=_jKylhJtPmI). He provides some really good advice on how to combat SQL Injection and make your app more effective. These are the key points from his video:
Don't use straight and unprotected SQL commands because this is the way hackers can perform a SQL Injection, stealing, modifying, or even deleting your data.
A good approach is to use the mysql_real_escape_string(String s) function. This basically places on the start of every dangerous character (/,", {, }, etc) a slash (/). So basically this makes the quote or slash inside the string useless.
The best thing to do is to use prepared statements. So, you basically say:
SELECT * FROM USERS WHERE username = ?
Later you go and replace the question mark with the string you want to input as the user name. This has the advantage of not confusing PHP or any other fault-tolerant language, and using this simple and (kind of, hacky) elegant solution to just say replace this with the string and tell the language that what is given is just a string and nothing more than that.
That is good and all, but this video is really outdated. It was made way back in 2013 and since then a lot of new technology has emerged. So, I tried to search the internet to find if there were any new approaches or if this is the one. But the problem was that either I couldn't find it or either I found something that was super confusing.
So, my question is: Is there a better and enhanced way to combat SQL Injection that has been introduced, or if prepared statements are still the norm and if they are vulnerable to any kind of attack?
Parameter binding is still the best solution in most examples of combining dynamic data with an SQL query.
You should understand why. It's NOT just doing a string substitution for you. You could do that yourself.
It works because it separates the dynamic value from the SQL-parsing step. The RDBMS parses the SQL syntax during prepare():
$stmt = $pdo->prepare("SELECT * FROM USERS WHERE username = ?");
After this point, the RDBMS knows that the ? must only be a single scalar value. Not anything else. Not a list of values, not a column name, not an expression, not a subquery, not a UNION to a second SELECT query, etc.
Then you send the value to be bound to that placeholder in the execute step.
$stmt->execute( [ "taraiordanov" ] );
The value is sent to the RDBMS server, and it takes its place in the query but only as a value and then the query can be executed.
This is allows you to execute the query multiple times with different values plugged in. Even though the SQL parser only needed to parse the query once. It remembers how to plug a new value into the original prepared SQL query, so you can execute() as many times as you want:
$stmt->execute( [ "hpotter" ] );
$stmt->execute( [ "hgranger" ] );
$stmt->execute( [ "rweasley" ] );
...
Are prepared statements the best? Yes, they are. It doesn't matter that the advice comes from 2013, it's still true. Actually, this feature about SQL dates back a lot further than that.
So are query parameters the foolproof way of defending against SQL injection? Yes they are, if you need to combine a variable as a value in SQL. That is, you intend for the parameter to substitute in your query where you would otherwise use a quoted string literal, a quoted date literal, or a numeric literal.
But there are other things you might need to do with queries too. Sometimes you need to build an SQL query piece by piece based on conditions in your application. Like what if you want to do a search for username but sometimes also add a term to your search for last_login date? A parameter can't add a whole new term to the search.
This isn't allowed:
$OTHER_TERMS = "and last_login > '2019-04-01'";
$stmt = $pdo->prepare("SELECT * FROM USERS WHERE username = ? ?");
$stmt->execute( [ "taraiordanov", $OTHER_TERMS ] ); // DOES NOT WORK
What if you want to allow the user to request sorting a result, and you want to let the user choose which column to sort by, and whether to sort ascending or descending?
$stmt = $pdo->prepare("SELECT * FROM USERS WHERE username = ? ORDER BY ? ?");
$stmt->execute( [ "taraiordanov", "last_login", "DESC" ] ); // DOES NOT WORK
In these cases, you must put the column names and syntax for query terms into your SQL string before prepare(). You just have to be extra careful not to let untrusted input contaminate the dynamic parts you put in the query. That is, make sure it's based on string values you have complete control over in your code, not anything from outside the app, like user input or a file or the result of calling an API.
Re comments:
The idea Martin is adding is sometimes called whitelisting. I'll write out Martin's example in a more readable manner:
switch ($_GET['order']) {
case "desc":
$sqlOrder = "DESC";
break;
default:
$sqlOrder = "ASC";
break;
}
I replaced Martin's case "asc" with default because then if the user input is anything else -- even something malicious -- the only thing that can happen is that any other input will default to SQL order ASC.
This means there are only two possible outcomes, ASC or DESC. Once your code has complete control over the possible values, and you know both values are safe, then you can interpolate the value into your SQL query.
In short: always keep in your mind an assumption that $_GET and $_POST may contain malicious content. It's easy for a client to put anything they want into the request. They are not limited by the values in your HTML form.
Write your code defensively with that assumption in mind.
Another tip: Many people think that client input in $_GET and $_POST are the only inputs you need to protect against. This is not true! Any source of input can contain problematic content. Reading a file and using that in your SQL query, or calling an API, for example.
Even data that has previously been inserted in your database safely can introduce SQL injection if you use it wrong.
Is prepared statement necessary when dealing with trusted data fetched from another query?
For example.
When a user is navigating throughout the site, they click named links like this: /?category=health where health is the value that is sent to the database.
In this scenario I of course use prepared statement like this:
$qry = $dbh->prepare('SELECT category_id, and, other, columns FROM categories WHERE query_value = ?');
$qry->execute([$_GET['category']]);
$get = $qry->fetch();
$qry = null;
But further down the script, I would display content associated with the selected category based on the categories.category_id fetched from the last query.
$Banners = $dbh->query('SELECT image FROM Banners WHERE category_id = '.$get['category_id'])->fetchAll();
I would like to think that this is a secure query.
That the value could be no other than a trusted value since it has to be a result from the previous query?
And this query won't be executed if the previous query doesn't return true.
Here's how I've done it so far:
It's a 3-liner. But it would speed up the coding part a bit if I was certain that the 1-liner above is fine too.
$qry = $dbh->prepare('SELECT image FROM Banners WHERE category_id = ?');
$qry->execute([$get['category_id']]);
$Banners = $qry->fetchAll();
What this all is about largely is about ensuring your SQL syntax is sane and what you think it is. Preventing malicious attacks is just a corollary of that. What if your category ids in the future spawn apostrophes or whatnot as part of the regular value? You need escaping for correct syntax then anyway, or, better, prepared statements.
Secondarily, there is second order injection, in which a value which was previously treated as unsafe is now suddenly treated as safe, even though it still has the potential for attacks. E.g. if your values legitimately contain apostrophes.
More broadly speaking: how do you ensure a value is "safe"?
// $get['category_id'] is safe and doesn't need escaping
'SELECT image FROM Banners WHERE category_id = '.$get['category_id']
Well, how do you know this? What code ensures this value is safe? Where does this value come from? Are you sure that's where it comes from? You're outsourcing the integrity of this query to some other part of the code here. How can you be sure that other part does its job correctly? Now, or in the future, after a lot of refactoring? Simply ensure yourself that you are producing correct queries by writing queries in a secure way, don't outsource your security.
You need to ask yourself: is this query prone to injection the way it is written? And the answer to the above example is a plain yes, it is prone to injection, and the safety merely depends on your trust that $get['category_id'] is what you think it is.
My question of to day is. Do i need to escape PDO in my script?
$columns = implode(", ",$column);
$query = ''.$query.' '.$columns.' FROM '.$table.'';
$dbh_query = $dbh->prepare($query);
$dbh_query->execute();
$dbh_querys = $dbh_query->fetchAll();
return $dbh_querys;
The whole script can be found at.
https://github.com/joshuahiwat/crud/blob/master/control/query_connector.class.php
Can someone explain why do i need a escape at this time or why not.
I like to hear from you, thanks a lot!
The parts of your query that are dynamic are the table name and column names. You can't use bind functions for these parts of the query. Bind functions can be used only for the parts of the query that would otherwise be a simple value in an SQL query. Like a numeric constant, or a quoted string or quoted date literal.
To avoid SQL injection from dynamic table names or column names, you have the following choices:
Use values that are predefined in your class, or otherwise certain to be safe. Don't use external content from users or any other source.
Use escaping. Note that the function PDO::quote() doesn't do the kind of escaping you need for table names or column names.
Create a "allowlist" of known table names and the column names for the respective table, and compare the dynamic input to the allowlist. If it doesn't match the allowlist, raise an error.
First of all you need to understand that the word you are using - "escape" - is meaningless.
What you probably mean is "to make your query safe from SQL injection". But, unfortunately, there is no such magic "escaping" that will make some abstract query safe.
The traditional query building assumes that all the query parts beside data values are hard-coded, while data values are bound via placeholders, like this:
$query = 'SELECT col1, col2 FROM some_table WHERE id = ?';
$stmt = $dbh->prepare($query);
$stmt->execute([$id]);
$row = $stmt->fetch();
This kind of a query considered safe.
In your case of a dynamically constructed query, every part is potentially vulnerable.
And here it is very important to understand that a burden of sanitizing all the query parts is entirely on this function. You cannot dismiss the danger simply claiming that your data is coming from the trusted source. That's a slippery ground because people often have no idea whether their source is trusted or not.
So, if take your question as "Do I have to protect this code from SQL injection", than the answer is - YES, YOU HAVE.
In the meantime you are protecting only a small part of your query - the data values. So you still have to protect (this term is much better than "escape") all other parts.
On a side note, your code is connecting to database every time it runs a query, which is highly inefficient and makes it impossible to use some database features.
Hey there, I'm doing some queries to a MySQL database that involves some user-input.. And I was wondering how to prevent injection attacks, like, for example, if someone were to type
"; a-MySQL query here;"
It would be executed, this would allow people access to compromise my system. I'm wondering how i could protect against things like this, possibly create a function that filters out the query looking for bad statements? or perhaps not allowing the ; character?
To sum it all up:
Whats the common practice now adays to prevent injection attacks?
How should I go about it?
How effective will this solution be against people who know what they are doing from changing my database.
The only way is to properly escape user-submitted data. Others have pointed out some ways of doing so.
There's another way: prepared statements and placeholders. Prepared statements are supported by every modern PHP database interface, including mysqli and PDO.
Let's use PDO as a demonstration. Let's say we wanted to update a bit of data in the table foo submitted by a user.
$sql = 'UPDATE foo SET bar = ? WHERE user_id = ?';
$sh = $db->prepare($sql);
$sh->execute(array( $_POST['bar'], $_SESSION['user_id'] ));
The variables in the array passed to execute replace the question mark placeholders in the query. When this happens, they are automatically escaped and quoted. You don't need to manually escape them to make them safe to put in the database!
On the other hand, you will still need to filter them for unexpected content, like HTML, Javascript, letters where you expect numbers, etc. Making data safe to insert into the database is only half of the battle.
An even better way than calling a mysql_escape_string variant is to use bound queries in PDO, so that it's impossible to forget or miss a case since PDO will do the escaping for you when circumstances require it.
See for more:
http://www.electrictoolbox.com/php-pdo-bound-placeholders/
They best solution is to run every piece of input from the wild (user) through this function PHP.net: MySQL Real Escape String This will clean up most - if not all - Injection issues seen today. You would simply need to do something like the following:
$var = mysql_real_escape_string($_GET['var']);
$query = "INSERT INTO foo (var) VALUES (\"$var\");
It's always good practice to force type casting on variables you know should be a type. For instance a numerical identifier:
$id = (INT)$_GET['id'];
or
$id = ( is_numeric($_GET['id']) ? (INT)$_GET['id'] : -1; // replace -1 with FALSE, or NULL, etc
Which will force that variable to be an Integer so you won't end up with $id being "foo" or some other non-numeric.
Is the any difference between writing
{$_GET['id']}
and
'".$_GET['id']."'
in a sql statement? both works the same
Its always a bad idea to put data from a get request directly into SQL, and there are many easy ways to prevent SQL injection.
For the simple case where you know you want a numeric ID, then you can simply force the value to be numeric and then there is no chance of SQL injection. One way might be okoman's usage of sprintf(), maybe something like this:
$sql = "INSERT INTO table VALUES(".sprintf("%d",$_GET['id']) .")";
though I think its ugly and bad form. Something like this is nicer:
$sql = "INSERT INTO table VALUES(".(int)$_GET['id']) .")";
Which simply casts the value to an int - if its not a number that can be cast to int - you'll get a "0" which may or may not be what you want.
Other alternatives (and for when your data is not supposed to be a number) include using escaping functions such as add_slashes() or the above mentioned mysql_real_escape_string().
IMHO, the best way to use database access from within PHP and very easily protect from SQL injection is to use the new PHP PDO library ( http://php.net/PDO ). This allows you to write SQL strings that contain no data whatsoever, and have the data added later inside the database server itself. In this form there is absolutely no way to do SQL injection.
Here is some example code:
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
$stmt = $dbh->prepare("INSERT INTO table VALUES (?)");
$stmt->bindParam(1, $_GET['id']);
$stml->execute();
As you can see, the data is not added to the SQL query itself and so no need for escaping.
Using either of these directly in a SQL statement is a VERY BAD IDEA. It allows for SQL injections. Be sure to sanitize your inputs using something like mysql_real_escape_string.
The main difference between the two is that the top can only be used inside a string that uses double quotes. The bottom, however, can be used with either double or single quotes.
As far as I know there's no difference, but should you be doing that? You're allowing unsanitised input into your sql query which means if your website is internet facing you're leaving it wide open to sql injection attacks.
Here's a Q&A you should read before going any further:
How can I prevent SQL injection in PHP?
If you use a variable - especially an associative array - in a string, you can be quite sure that it will lead to errors. It's just bad style.
I - personally - don't like the second alternative either.
sprintf( '... %d ...', $_GET[ 'id' ] );
That's my favorite way of putting a variable into a string.