Preventing SQL Injections on INSERT-only queries. Is it a big deal? - php

This is my first time creating a PHP form that will run a MySQL query using INSERT INTO to store data in a production DB. Will this pass for "secure" or is it over-kill?
$orderText = $mysqli->real_escape_string(stripslashes(htmlentities((!isset($_POST["order_text"])?"undefined":$_POST["order_text"]))));
$stmt = $mysqli->prepare("INSERT INTO testtable (order_text) VALUES (?)");
$stmt->bind_param('s',$orderText);
$stmt->execute();
I'm not sure how the lack of a SELECT * affects the amount of risk I'm opening myself up to, but it seems like a script that only uses INSERT is safer. True?

There is a great amount of false assumptions in your question.
It is certainly an overkill.
Let's examine your extremely-hard-to-read zillion-nested-operator statement:
storing word 'undefined' makes no sense. A database has a special mark for the undefined fields - a NULL value. Or simply an empty string would be enough.
unconditional stripslashes adds nothing to security but may spoil the data.
htmlentities has nothing to do with SQL security, may help with site security in other aspects and may spoil the data as well.
escaping adds nothing to security and will spoil the data.
You are taking the problem from the wrong end.
Your primary goal is to format your query properly. Not to defend from imaginary "attackers" but to privent malfunction with most honest data. While properly formatted query will be invulnerable to various attacks just as a side effect.
Say, real_escape_string has nothing to do with security. It is used merely to format strings. There are no strings (data enclosed in quotes) in your query - thus this function is utterly useless (and even harmful).
In fact, an injection via INSERT is no less disastrous than via SELECT.
Finally, the right code would be
$stmt = $mysqli->prepare("INSERT INTO testtable (order_text) VALUES (?)");
$stmt->bind_param('s',$_POST["order_text"]);
$stmt->execute();
and when printing the order text back to the site, use htmlspecialchars()
that's all.

Variable binding, which you do on line 3, is sufficient to prevent injection attacks in general. Binding is a good idea and, in my opinion, should always be done. It has not only security advantages, but can yield a performance boost as well.
I would argue that performing the extra parsing at line 1 is actually a disadvantage:
It increases complexity, and some attacks take advantage of known data transformations, though using binding mitigates those as well.

I will recommend to treat everything that comes from a client, your visitors, as threat. Don't relax and only focus on some sql queries. Practicing a good habit has no limitation.

I agree with Charles, by binding the param you are already properly escaping the variable removing the chance of a SQL injection attack, and the complexity of line 1 is overkill. This will be come evident when you make the jump to PDO, because there is no specific $dbo->escape() call, as the escaping work is already completed with the prepare() / bind() calls.

Related

Not using php prepare statement when I am not getting an input from a user

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.

PHP MySQL injection security: Does verifying that $_GET input is numeric provide enough protection?

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.

filter_input and mysqli_real_escape_string for integers

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

How to secure project against hacks and attacks?

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!

does addslashes(stripslashes($field)) guarantee sql injection invulnerability?

Minus the whole addslashes() vs mysqli_real_escape_string() argumentation will stripping then adding slashes guarantee sql injection invulverability? Will this alter the data in anyway, for example displaying the string with double slashes after fetching it from the database?
so what you want to do is
$input='bla" SELECT * FROM blabla"';
$escaped=stripslashes(addslashes($input));
in that case
$input==$escaped is true
so no this would probably do nothing
thats why you should prefer mysql_real_escape_string
Escaping characters (addslashes()) may protect you from SQL Injection. I'm not an expert on how to sanitize inputs, and here's why:
I skipped the whole "sanitizing" thing and went straight to prepared statements. Sanitizing / escaping means you have to do the reverse on the output side, which means double the effort every time, and double the chances to mess up somewhere - allowing bad input in. If you just plop the PDO between every database query you do and the database itself, your worries are over.
That's not to say, of course, that the PDO protects you from attacks like CSRF or XSS, but the actual stored values are SQL-injection-safe, and you can strip html or whatever you need to do before you store it to protect from attacks like those.
NO
use: mysql_real_escape_string.
Why: you are not considering a ton of issues, mainly encoding of strings, etc...
No, having the right amount of slashes helps with some vulnerabilities, but you still need to check user input. There is no guarantee sql injection invulnerability, ever.
addslashes() will protect you in most cases. As for the getting the output, it depends how your submitting it, if you do
$input = addslashes("Bob's shoes")
you'll get Bob\'s shoes.
When you put this in your database
insert into tbl (txt) values (Bob\'s shoes)
The output of
select txt from tbl
will be Bob's shoes as you intended, the slashes are removed by the sql on insert.
If your anal about it you can say add other precautions, but if you want a quick and easy thing that's not a ridiculously secure website it should be fine. there's also built in php sanitize functions if you look them up

Categories