I do know of parametrized queries, but I've got a couple of old sites where I've used this method for cleaning user input:
<?
mysql_query( sprintf( 'SELECT col1 FROM table1 WHERE id = %d', $tainted ) );
?>
Note that there are no quotes around %d. Is there any value for $tainted that could cause such queries to have unexpected results? It's easy to give syntax errors, but I don't care too much about that.
I've used the same method for similar UPDATE and DELETE queries. Should I bother going back and fixing all old queries, or is there no vulnerability here?
No, it's not a vulnerability.
But it is a potential vulnerability: if some maintenance programmer decides to perform a small change and forgets that the variable may be tainted, or if the variable's data type is changed from integer to string (and the specifier to %s) down the road -- then there will be trouble.
It's better to simply not go there to begin with (but from a practical point of view, it's also not apparent if beefing up the defenses of this legacy code is worth it).
To answer your question, using sprintf in the manner which you presented, should avoid any sql injection, because any non-int values being cast as decimal via %d will simply take on a value of 0:
$ -> php -r "echo sprintf('update table where id = %d', 'drop databases');"
update table where id = 0
However I would be remiss if I did not attempt to persuade you to use the PDO API, and more specifically, prepared statements. I believe the mysqli API also has prepared statements, but I have never used them, only ever used PDO prepare, or an ORM such as Doctrine or Propel.
It appears safe enough as an input, but it's certainly not advisable from a user experience point-of-view, since you won't be able to provide the user with details if the input doesn't match the parameters needed if the id attribute.
In theory, it shouldn't be vulnerable, because sprintf() won't insert the variable if it isn't numeric.
However, that doesn't mean it's right. It may still result in errors that could break your code.
Regardless of whether this is vulnerable or not, I would strongly recommend switching away from the mysql_xx() functions entirely.
The old MySQL library has been deprecated (see the notes on the PHP manual pages), and it is recommended to switch to either PDO or mysqli_xx libraries.
Both the PDO and MySQL come with a feature called Prepared Queries, which does exactly what you're trying to achieve here, and is a far better solution than sprintf() for this kind of thing. Removes the need to escape all your variables for SQL too.
Hope that helps.
Related
P.S: I know it is a best practice to user prepared statements all the time, and it should be a habit to use prepared statements. So putting this aside, please just let me know technically what can go wrong if I don't write below scenario with prepare.
1. I am not getting any input from users or any other class file, and I won't in the future.
2. I am getting an input from a variable in the same PHP file (an array for example).
Example: ($myID will be a variable hardcoded in the same PHP file)
$myID=12;
$wpdb->query("UPDATE `$table_name` SET `your_column_1` = 1 WHERE `myTable`.`your_column_id` = $myID");
Tl;DR This is a very bad idea. You are introducing long-term risk to save a few seconds of coding effort. It is overwhelmingly likely that you will, sooner or later, introduce a SQL injection risk as your code and data evolve.
If you:
are extremely careful about validating your data,
are extremely careful about constructing your queries,
are absolutely certain that your data is safe and free of any user input (from any source at all, including forms, sensors, APIs, scraping websites, etc.), and
are absolutely certain that no one will ever modify your data or your code (or reuse your code inappropriately), including you,
Then we can say:
it would be safe to execute queries without prepared statements, and - and this is the important part -
you would be living in a fantasy world.
You can never assume safely that you can avoid preparing statements. It’s highly, highly likely that your code will break if you do. You could have bad data, overlook a problem, change the code yourself somehow, repurpose the code for something insecure, or make any number of other errors. You may end up doing harmless SQL injection yourself (with, say, a name or spelling like O’Brien) or you may get massively hacked...
...all to save a few seconds of coding time.
Short version: either (1) use prepared statements on every query that has any kind of variable information or (2) learn the hard way why that’s the rule.
This is a wordpress plugin and will only be used by me in admin panel. So the problem caused because of using "in" statement because it is hard to write the query like update column where color in ('black','white').
If you're developing for WordPress, have you considered using the wpdb API? It makes it pretty easy to add parameters to your SQL queries.
Example of using parameters for an IN( ) predicate:
$colors_array = ["black", "white"];
$placeholders = array_fill(0, count($colors_array), "%s");
$placeholder_list = implode(",", $placeholders);
$wpdb->query( $wpdb->prepare(
"
UPDATE $wpdb->stock
SET quantity = 327
WHERE color IN ($placeholder_list)
",
$colors_array
));
See https://codex.wordpress.org/Class_Reference/wpdb#Protect_Queries_Against_SQL_Injection_Attacks
I agree with Ed Cottrell's advice that you should not compromise on secure programming methods. Use the most secure method and use it consistently.
You don't have to waste time thinking about whether any given case is "safe enough" to skip using the secure method.
You don't have to worry if it's still safe after your PHP variables are no longer hard-coded.
You don't have to worry that someone will copy & paste your code as an example, and they use it in an unsafe way.
This question already has answers here:
Why is using a mysql prepared statement more secure than using the common escape functions?
(7 answers)
Closed 9 years ago.
Okay, I still don't really get it. I keep reading that in order to properly escape your MySQL queries, you need to use mysqli_prepare() and mysqli_bind_param().
I tried using this setup and, quite frankly, it's a little clunkier. I'm stuck passing variables by reference when I don't need to ever reference them again, and it's just more lines of code to accomplish the same task.
I guess I just don't get what the difference is between:
<?php
$sql = new \MySQLi(...);
$result = $sql->query('
UPDATE `table`
SET
`field` = "'.$sql->real_escape_string($_REQUEST[$field]).'";
');
?>
and
<?php
$sql = new \MySQLi(...);
$stmt = $sql->prepare('
UPDATE `table`
SET
`field` = ?;
');
$value = $_REQUEST[$field];
$stmt->bind_param('s', $value);
$stmt->execute();
$result = $stmt->get_result();
unset($value);
?>
other than more code.
I mean, did they implement this so that people wouldn't forget to escape values before sending them in a query? Or is it somehow faster?
Or should I use this method when I intend to use the same query repeatedly (since a mysqli_stmt can be reused) and use the traditional method in other cases?
What you are reading, that you need to use mysqli_prepare() and mysqli_bind_param() functions to "properly escape your MySQL queries" is wrong.
It is true that if you use mysqli_prepare() and mysqli_bind_param(), you needn't (and shouldn't) "escape" the values supplied as bind parameters. So, in that sense, there's some truth in what you are reading.
It's only when unsafe variables are included in the SQL text (the actual text of the query) that you need to "properly escape" the variables, usually by wrapping the variables in mysqli_real_escape_string() function calls.
(We note that it's possible to make of use of prepared statements and still include un-escaped variables in the SQL text, rather than passing the variable values as bind_parameters. That does sort of defeats the purpose of using prepared statements, but the point is, either way, you can write code that is vulnerable.
MySQL now supports "server side" prepared statements (if the option is enabled in the connection), and that's a performance optimization (in some cases) of repeated executions of identical SQL text. (This has been long supported in other databases, such as Oracle, where making use of prepared statements has been a familiar pattern for, like, since forever.)
Q: Did they implement [prepared statements] so that people wouldn't forget to escape values before sending them in a query?
A: Based on the number of examples of code vulnerable to SQL Injection when not using prepared statements, despite the documentation regarding mysql_real_escape_string() function, you'd think that certainly would be sufficient reason.
I think one big benefit is that when we're reading code, we can see a SQL statement as a single string literal, rather than a concatenation of a bunch of variables, with quotes and dots and calls to mysql_real_escape_string, which isn't too bad with a simple query, but with a more complex query, it is just overly cumbersome. The use of the ? placeholder makes for a more understandable SQL statement,... true, I need to look at other lines of code to figure out what value is getting stuffed there. (I think the Oracle style named parameters :fee, :fi, :fo, :fum is preferable to the positional ?, ?, ?, ? notation.) But having STATIC SQL text is what is really the benefit.
Q: Or is it somehow faster?
As I mentioned before, the use of server side prepared statements can be and advantage in terms of performance. It's not always the case that it's faster, but for repeated execution of the same statement, where the only difference is literal values (as in repeated inserts), it can provide a performance boost.
Q: Or should I use this method when I intend to use the same query repeatedly (since a mysqli_stmt can be reused) and use the traditional method in other cases?
That's up to you. My preference is for using STATIC SQL text. But this really comes from a long history of using Oracle, and using the same pattern with MySQL fits naturally. (Albeit, from Perl using the DBI interface, and Java using JDBC and MyBATIS, or other ORMs (Hibernate, Glassfish JPA, et al.)
Following the same pattern just feels natural in PHP; the introduction of mysqli_ and PDO are a welcome relief from the arcane (and abused) mysql_ interface.
Good code can be written following either pattern. But I challenge you to think ahead, about more complex SQL statements, and whether the choice to use mysqli_real_escape_string() and concatenating together a dynamic string to be executed, rather than using static SQL text and bind parameters, might make reading, and deciphering, the actual SQL being executed more complicated for the soul that finds themselves maintaining code they didn't write.
I think studies have shown that code is read ten times more than it is written, which is why we strive to produce readable, understandable code, even if that means more lines of code. (When each statement is doing a single identifiable thing, that's usually easier for me to understand than reading a jumble of concatenated function calls in one complicated statement.
I think it's less a question of the latter method being more secure per se than encouraging separation of logic. With prepared statements the SQL query is independent of the values we use. This means, for example, when we go back and change our query we don't have to concatenate a bunch of different values to a string, and maybe risk forgetting to escape our input. Makes for more maintainable code!
There are a couple main benefits I found that were well written:
The overhead of compiling and optimizing the statement is incurred
only once, although the statement is executed multiple times. Not
all optimization can be performed at the time the prepared statement
is compiled, for two reasons: the best plan may depend on the
specific values of the parameters, and the best plan may change as
tables and indexes change over time.
Prepared statements are resilient against SQL injection, because
parameter values, which are transmitted later using a different
protocol, need not be correctly escaped. If the original statement
template is not derived from external input, SQL injection cannot
occur.
On the other hand, if a query is executed only once, server-side prepared statements can be slower because of the additional round-trip to the server. Implementation limitations may also lead to performance penalties: some versions of MySQL did not cache results of prepared queries, and some DBMSs such as PostgreSQL do not perform additional query optimization during execution.
Source
I would like to add that mysqli_bind_param() has been removed as of PHP 5.4.0. You should use mysqli_stmt_bind_param()
I'm developing a simple PHP database application for internal use but would like to code it to best practices. Some of my pages are receiving integer values from GET requests and I'm just wondering how much validation and sanitation is really required.
Currently I'm using $num = filter_input(INPUT_GET, 'num', FILTER_VALIDATE_INT, $num_options); with specified min and max values. From here I'm exiting with an error message if $num == false
Is it necessary to also use $mysqli->real_escape_string($num);
Currently I am not bothering because I think it's quite hard to do SQL injection using an integer...
Thanks,
Kevin
UPDATE: To clarify the query I'm doing looks like this
$sql = "SELECT employeeID, concat(FirstName, ' ', LastName) as Name FROM employee WHERE employeeID='$num'";
I see your using mysqli, your best option for security is to look into Prepared Statements.
PHP mysqli Prepared Statements
It's a bit involved for an example, but the above link has indepth examples.
Once you get the hang of it though, and build your class. It's really only a normal sql query but instead of including your values you use ?
"SELECT * FROM account WHERE username = ? AND password = ?"
and you bind your values to the statement:
array("bradley", "Passw0rd");
The security comes from, as a short answer, is the fact you don't concat the values into the query string yourself. Making it less prone to sql injection.
Like many other PHP users you are taking escaping wrong. You taking it as a some sort of magic wand which makes some "evil characters" "safe".
This is wrong idea.
though prepared statements can be taken as a sort of such a magic wand, escaping is not a synonym for "SQL injection protection". It is a merely string syntax rule - no more, no less.
Is it necessary to also use $mysqli->real_escape_string($num);
It is irrelevant question.
To escape or not to escape decision have to be bound to SQL, not to the data source or any validations:
real_escape_string() have to be used for the sql strings, i.e. parts of the query enclosed in quotes. Have to be used unconditionally, despite of whatever previous manipulations.
For the any other part of the query real_escape_string() being completely useless.
An explanation:
Data validation rules can be changed.
While SQL building rules have to be explicit and unconditional. To make a developer never ask himself a question like this.
In fact, it's completely different matters: data validation and query building. Why keep in mind such details and build the query accordingly? Why not to build the query based on some set of general purpose rules, irrelevant of the data nature at all?
So, to your question again:
if you are adding your data to the query as is, without quotes, real_escape_string() going to be completely useless in this case, but casting/validation become essential.
if you are adding your data to the query using prepared statement, real_escape_string() going to be completely useless and even harmful.
if you are adding your data to the query in quotes - you ought to do real_escape_string() in this case.
it is also worth to mention that if you are adding your data to the query as a part of SQL language - as an identifier or an SQL keyword - real_escape_string() is completely useless too, as well as prepared statement. Whitelisting is your only friend here
I have started in web development not long time ago. I know some stuff now, but I'm really concerned about security issues that may arise. I know simple security solutions like preg_replace , but I'm not confident with that.
So I would like to ask you for any sort of speaking "universal" security standards that can be applied in the following cases. As I mentioned, I'm not pro so it would be great if you can start with something simple, yet useful. If possible could you provide examples please?
I did have a look at php manual, although I would like to know additional info from person.
Here are some typical MySQL / PHP things I use in my projects. Could you suggest any improvements to make them more secure?
$sql = mysql_query("SELECT * FROM stories WHERE showing = 1 ORDER BY cr_date DESC LIMIT 5") or die (mysql_error("There was an error in connection"));
while($row = mysql_fetch_assoc($sql)){
$story_id = $row["id"];
// etc...
}
$username = $_POST['username'];
$sql = mysql_query("INSERT INTO myMembers (username, //etc... )
VALUES('$username' //etc.. ")or die (mysql_error());
$username = $_GET['username'];
//gets username from url like http://myweb.com/profile.php?username=blabla
First of all, thank you for caring about web security. Many PHP developers don't know anything about it, and don't care to learn. They are the ones who are exposing our passwords and bank accounts to hackers. Be part of the solution! :-)
1. Treat the mysql extension as if it is deprecated.
Use the PDO or mysqli extensions instead. The plain mysql extension does not support prepared statements, and some other features, such as transaction control. No one should be using mysql if they have PDO_mysql or mysqli available to them.
2. Do not interpolate external data into SQL.
Anytime you get a value from $_GET or $_POST, you should consider it to be unsafe to use in any SQL statement, or shell_exec(), or other instance where you execute the string as some kind of code.
3. Use prepared query parameters instead of interpolation.
It's really easy. In fact, it's easier to use query parameters than it is to interpolate variables into SQL strings. You don't need to worry about escaping, or long complex string-concatenation.
See example code here: http://us.php.net/manual/en/pdo.prepare.php
4. For corner cases, use careful filtering.
A query parameter takes the place for one literal value in an SQL expression. Not table names, not column names, not SQL keywords, not lists of values or full expressions. For those, you do need to use string interpolation, but see my presentation SQL Injection Myths and Fallacies for examples of how you can "whitelist" values to interpolate.
Also check out the PHP filter extension, which offers a flexible way of validating inputs or stripping off invalid characters to make sure only the valid part of the input is used.
Looking at your examples, the SELECT query has no dynamic values interpolated from external sources like $_GET. So that one is safe.
The INSERT query takes a value from the request, which could contain malicious content that changes the way your query runs. This one is a good candidate for using query parameters.
Also consider that SQL injection is one of the two most prevalent security issues with PHP. The other one is Cross-Site Scripting (XSS). This is not directly related to SQL, but you should learn about it too.
Here's a good resource for learning more about web programming security: OWASP.org cheat sheets.
Many frameworks have a good set of security measures already in place that will do a great deal in preventing things like SQL injections. Yii, CakePhP, CodeIgnitre all may be of some use.
Although it's almost impossible to beat Bill, I feel I must clarify answers stating that "you have to trust no user input".
In fact, quite contrary - SQL injection protection will do any good only if it would be totally ignorant of the data source. And treat ALL the data as potentially malicious. And process it accordingly.
Thus, to summarize all the advises:
Prepared statements is a good approach but not a complete one.
It has a brilliant idea of using some placeholder, a proxy to represent the actual value in the query. Thus this value can be properly sanitized.
But these placeholders, as Bill said, are limited to the strings and numbers only. So, it would be a good idea to add another placeholder of your own - for identifiers. But you still have to watch out SQL syntax keywords manually.
So, instead of "Do not interpolate external data into SQL." statement one have to use
"2. Do not interpolate values into query directly but only by some proxy, performing necessary precautions"
The most important thing to remember is never trust anything from an external source (eg user input, responses from other web services etc). Always sanitise all input and where possible use code from your database provider to do so.
In the case of MySQL parameterising all queries is essential, so use a prepared statement, eg
$statement = $db->prepare('SELECT * FROM stories WHERE title = :title');
$statement->execute(array(':title' => $title));
$rows = $statement->fetchAll();
Your current insert statement is vulnerable to an SQL injection attack, modify it to be closer to:
$username = $_POST['username'];
$statement = $db.prepare("INSERT INTO myMembers (username) VALUES(':username');
$statement->execute(array(':username' => $username));
You should also ensure that you never store any passwords in plain text, always store a hashed version of a password (along with a salt) and check that the hash matches rather than the actual string. This means that should your database become compromised, figuring out your user's credentials becomes a non-trivial task.
These are only a couple of ways of making your app more secure, I'd highly recommend reading OWASPs top 10 site vulnerabilities and researching these individually as each one in itself is quite a big topic!
PDO and prepared statements are still kind of confusing to me, no matter how much I read about them so far. So I know they are more "secure" but is it really that important? I mean I can get the same end result using basic mysql with mysql_real_escape_string() and htmlspecialchars() right?
You could, but PDO and prepared statements are the absolute safest. Could you do it by hand and use the mysql_real_escape_string() function? Sure. In fact, your output might look identical. But in the end, the code that PDO would require would be a hell of a lot shorter than the code if you had done it manually.
Also, if you aren't using prepared statements, you run the risk of human error: say you forget to escape a value or sanitize an input. Mixed in with all of your other code, the one line that isn't properly sanitizing could crop up to be a nightmare down the road.
Hope this helps!
I really like the PDO interface. Once you get used to it, it's a lot cleaner than the mysql_* function style. It took me a while to figure it out, too, but it's worth it.
The part that I found confusing was remembering what methods belong to the PDO DB connection object itself, and which are part of the statement objects.
With certain actions, you'll get a performance benefit from repeating prepared statements, too. For instance, if you're doing a bunch of inserts in a loop, you can prepare the statement, and then bind new data in the loop each time before inserting.
The security is much better too, in that you are relying on a well tested library to escape your data on each insert. It's like cryptography - why do it yourself, when it's something this important? There's no reason to give yourself a chance to get it wrong (i.e., accidentally miss escaping something inserted into a query).
I recommend this guide to PDO, from the author who wrote this excellent giant book on Mysql.
I like to use the positional parameter style, and then you just make an array of the data and pass it in. My queries look like
$pdo_db=pdo_connect('cow_db');
$sql='select this,that,count(those) as snout from lovely_table where name=? and horses=?';
$data=array($username,$horse_count);
$query_stmt=$pdo_db->prepare($sql);
$result_handle=$query_stmt->execute($data);
//then I have a function to load data from the result handle
$info=load_array($result_handle);
You could make functions like this to work with the standard php mysql interface, but why not just use PDO?
I agree with others who say using prepared queries is generally better than using escaping functions. It's easier to use correctly, and there's no way that parameter values can introduce SQL injection problems, since the value are sent to the RDBMS server separately from the SQL query.
However, using parameters is useful only when the dynamic parts of your SQL query is a parameter in lieu of a literal value in an expression. You can't use a query parameter in place of a table name, column name, an SQL expression, or a list of values (e.g. arguments of an IN( ) predicate).
Prepared queries also have better performance than non-prepared queries (at least in MySQL). Most people say the opposite, but the well-respected mysqlperformanceblog.com did the testing:
http://www.mysqlperformanceblog.com/2006/08/02/mysql-prepared-statements/
As long as you sanitize data appropriately for your queries then you don't have to use PDO/prepared statements. Though, I would personally recommend using PDO/prepared statements simply because they do both make development/debugging easier and prepared statements prevent incorrect data types from even getting in to a query.
If you want to learn more about how to create a simple prepared statement look in to the function sprintf. You simply replace any variable strings, integers, etc with a type specifier (in this case %s and %d respectively).
So for example in the following query, I know that id is going to be an integer (it will be numerical) and name will be a string (alphanumeric).
$username = 'Simon';
$id = 3;
$query = "SELECT FROM `users` WHERE `id` = {$id} AND `name` = '{$username}'";
If I'm getting either of this variables from an un-trusted source (such as a POST/GET) then I can make sure they are the correct data types by replacing the final line (the $query set) with a sprintf call like this:
$username = 'Simon';
$id = 3;
$query = sprintf( "SELECT FROM `users` WHERE `id` = %d AND `name` = '%s'", $id, $username );
sprintf will simply not let me use a string for the $id or an integer for the $name when it is called which ensures the correct types of data are given (this gives me that little extra bit of security). IF incorrect data types ARE given then I believe it'll cast the variables to the requested type.
To read more about sprintf visit here: http://php.net/sprintf
I hope this explains enough (it's my first answer) :).
you could try zend_db which uses pdo under the hood. (mdb2 is another option you can use.)
PHP lets you do anything you want, and it just so happens PDO does what "you want" with a lot less code. :)