PHP SQL Injection is it safe? - php

I want to know if my code is 100% secure agasint SQL injection, it looks like this:
$table = $_GET['table'];
switch ($table) {
case 'data':
$sql = "select * from $table";
break;
case 'anothertable':
$sql = "select * from $table";
break;
}
$con = new mysqli($hostname,$username,$password,$db_name);
$result = $con->query($sql);

If $table is not matched by the switch, you haven't set $sql at all...
Otherwise, you have avoided the risk of injecting bad things via $table by whitelisting the acceptable table names.
One point I would make is that one has to read quite closely to see that $table has changed from an untrusted input to a validated table name. So anyone coming to your code in future may think
that you have a problem here that needs fixing, or
that interpolating variables into SQL queries is generally acceptable.
So probably worth going out of your way to explain in comments what you're doing, and (more importantly) why.

In your place I would avoid such a dynamical querying based on the user input, as it smells of a bad application design,
But in regard of SQL injection your code is safe, as it's rightfully implementing the only proper strategy possible - the whitelisting.

Your code is not safe. Here is an example of how you would make it safe with MySQL (PDO). I'm doing it in PDO because that is what I use often and not mysqli :)
$table = $_GET['table'];
$valid_command = FALSE;//value becomes true if you find your
//value in the following if conditional
if (($table==='table')||($table==='anothertable'))
{
$valid_command = TRUE;
}
if ($valid_command)
{
$sql = "SELECT * FROM table";
$sqlPrepared = $conn->prepare($sql);
$sqlPrepared->bindParam(':table', $table);
$sqlPrepared->execute();
}
If you wish to do this in mysqli, it is easy to adapt it. Hope that helps.

Related

Is it a good practice and safe way to create user defined function with mysqli?

Am trying to change my project from mysql to mysqli. I have my db connection as:
$link = mysqli_connect($hostname, $username, $password, $database);
if(mysqli_connect_errno()) {
echo "Opps! Connection could not be established: ", mysqli_connect_error();
exit();
}
Then I have a user defined function as:
function get_name($id) {
$query = mysqli_query($link, "select name from staff where id='$id'");
$result = mysqli_fetch_assoc($query);
return $data = $result['name'];
}
I understand that I have to declare $link as global (as shown below) which work fine.
function get_name($id) {
global $link;
$query = mysqli_query($link, "select name from staff where id='$id'");
$result = mysqli_fetch_assoc($query);
return $data = $result['name'];
}
My question here is: Is it a good practice and is it safe?
I wouldn't necessarily call it a good or bad practice to write a function with such a specific purpose, just what fits your needs. If you plan to do this exact task in multiple places throughout your code, a function is useful to make your code easier to read and avoid repeating yourself.
As far as safety goes, you need to sanitize inputs before using them in a query. For the case of an integer $id field, you could simply cast it as an integer $id = (int)$id;. For other data types, you would want to escape it by using $id = mysqli_real_escape_string($link, $id);. Then you'll be safe.
I would also advise that you look into PDO instead of mysqli. I believe it's much more commonly used these days.
Taken literally, this question makes very little sense. User-defined functions almost always are good practice, no matter if you are using them with mysqli or any other API. Yet functions in general have nothing to do with safety.
While speaking of the code provided, it is not safe because of lack of prepared tatements.
So, to make your code proper, you have to create functions to handle mysqli queries with parameters first. And then employ these functions in your own helper functions, to make them look like this:
function get_name($id) {
return dbgetOne("select name from staff where id=?",[$id]);
}
as you can see it will not only make your queries safe, but also shorten your code.

MySQL Injection Confusion, How Does it Actually Protect?

I've been reading about MySQL Injection as I've just started adding prepared statements to my site. I wanted to know a bit more about how it actually protects and after reading some, I don't think I've done it correctly.
I was reading from here: http://www.tizag.com/mysqlTutorial/mysql-php-sql-injection.php
I really don't understand how it can be 'injected' or how prepared statements get around it. Particularly this bit:
Normal: SELECT * FROM customers WHERE username = 'timmy'
Injection: SELECT * FROM customers WHERE username = '' OR 1''
When using SELECT, the things I use for WHERE are only ID or username. They can't change their ID and the username is validated when they sign up by this:
function protect($string) {
global $con;
return mysqli_real_escape_string($con,strip_tags(addslashes($string)));
}
So is this "protect()" preventing MySQL injection?
If so, what are the uses for prepared statements? Is this protection correct?
Instead of
$car = 'nissan';
$update_car = mysqli_query($con,"UPDATE stats SET stats.car = $car WHERE stats.id = 4");
I put:
$car = 'nissan';
$query = "UPDATE stats SET stats.car = $car WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
If that is correct, I don't understand how that does anything besides just adding another line of code?
EDIT
First prepared statement, any good?
$getusername = mysqli_prepare($con, "SELECT users WHERE users.username = '",mysqli_real_escape_string($username),"'");
Firstly, let's look at your attempt at escaping:
function protect($string) {
global $con;
return mysqli_real_escape_string($con,strip_tags(addslashes($string)));
}
There are 2 mistakes here: Firstly, addslashes is doing the same job as mysqli_real_escape_string (but doing it badly), so remove that.
Secondly, you should always sanitise for the context you're working in. So, when generating SQL, you sanitise for SQL; when generating HTML, sanitise for HTML. So putting strip_tags in here doesn't make sense, because it's to do with HTML, but this is apparently an SQL escaping function. Do that separately, when you're preparing output in your templates.
In short, just mysqli_real_escape_string on its own would be better than what you have here.
Next, let's look at parameterising the query:
$car = 'nissan';
$query = "UPDATE stats SET stats.car = '$car' WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
This statement isn't prepared or parameterised - as far as the database is concerned, it's still just a string of SQL. If $car is actually set from user input (e.g. $car = $_GET['car'];), then you can insert any piece of SQL in the middle of the query:
$car = "nissan'; DROP TABLE stats; --";
$query = "UPDATE stats SET stats.car = '$car' WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
// MySQL will now drop your table; oops!
// UPDATE stats SET stats.car = 'nissan'; DROP TABLE stats; --' WHERE stats.id = 4
Adding $car = mysqli_real_escape_string($car); will correctly escape the ' in the input, stopping it ending the string and starting a new SQL statement.
Parameterised queries avoid the problem a different way: they tell the database which parts of the query are supposed to be SQL, and which are just data provided by the user. It looks like this:
$car = "nissan'; DROP TABLE stats; --";
$query_with_placeholder = "UPDATE stats SET stats.car = ? WHERE stats.id = 4";
// Note the ?, without quotes, represents somewhere for data to be put by MySQL
$prepared_statement = mysqli_prepare($con, $query_with_placeholder);
// Now we tell MySQL what to put in the placeholder
mysqli_stmt_bind_param($prepared_statement, 's', $car);
// And execute it
mysqli_stmt_execute($prepared_statement);
Because $car is never actually inserted into the SQL, just passed as a separate parameter, there is no way of it injecting anything nasty, and we don't need to do any additional escaping.
Also note that mysqli_real_escape_string is to prevent injection with strings. If you have a (for example) integer field then escaping the passed variable to cope with a quote doesn't really help as injection doesn't require the quote to work.
For example:-
$some_field_expected_to_be_int = '1 OR 1=1';
$query = "SELECT * FROM users WHERE id = ".mysqli_real_escape_string($con, $some_field_expected_to_be_int);
would still give a piece of SQL reading:-
SELECT * FROM users WHERE id = 1 OR 1=1
and return everything.
In such a case you should make sure it is an integer:-
$some_field_expected_to_be_int = '1 OR 1=1';
$query = "SELECT * FROM users WHERE id = ".(int)$some_field_expected_to_be_int;
Personally both prepared statements and escaping fields have their places. Prepared statements make it harder to forget to escape items and with many databases they give a performance advantage, but the mysqli placeholders are not readable, getting a statement out for debugging is not workable and it becomes a nightmare when used for heavily dynamic sql. But escaping leaves you with the risk you will forget somewhere and the code can look messy.
EDIT - to add a bit on prepared statements.
We use a couple of in house database classes which separate things a bit from mysql / mysqli / pdo / sql server, but for a basic example of using prepare and a bound parameter:-
<?php
if($stmt = $this->db->prepare("SELECT users WHERE users.username = ? "))
{
$stmt->bind_param('s', $username);
$stmt->execute();
}
?>
In this the SQL is not built up using the variable (which could have been manipulated), rather the variable is passed as a parameter.
Escaping the data and using prepared queries both protect against SQL injection. But escaping is more error-prone, because it's easy to forget to call it in all the places you need it. If you use prepared statements, the protection happens automatically.
There's no significant difference between
$update_car = mysqli_query($con,"UPDATE stats SET stats.car = $car WHERE stats.id = 4");
and
$query = "UPDATE stats SET stats.car = $car WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
This is just a stylistic choice. The second version can be useful when you want to insert debugging statements, e.g.
$query = "UPDATE stats SET stats.car = $car WHERE stats.id = 4";
echo $query;
$update_car = mysqli_query($con,$query);
Popular practice is to both sanitize input and use prepared statements with your queries whenever you have a possible user input variable.
Sanitation should also prevent users from overflowing your variables and upload large quantities of data to your application.
Preparing statements is your second measurement of security, it prevents mistakes you might make later, like forgetting to escape input or not realising what is actually user input next time you refactor your application.
Any time you don't properly guard against injection, you open your project up to users deleting data, modifying data or uploading malicious code to your application. (Do you really want someone to upload javascript in a textfield?)

Do I sanitize/escape correctly?

I've made a simple search-script in PHP that searches a mySQL database and outputs the result. How this works is like this:
User searches for "jack's" through a search-form.
My PHP-script GETs this search, and sanitizes it.
Then the script, with the use of SELECT and LIKE, gets the results.
The script then outputs the result to the user.
Lastly, the script tells the user that "jack's returned x results." with the help of escaping.
What I would like to ask is, am I doing it right?
This is how I sanitize before SELECTING from the database:
if(isset($_GET['q'])){
if(strlen(trim($_GET['q'])) >= 2){
$q = trim(mysql_real_escape_string(addcslashes($_GET['q'], '%_')));
$sql = "SELECT name, age, address FROM book WHERE name LIKE '%".$q."%'";
}
}
And this is how I escape before outputting "jack's returned x results.":
echo htmlspecialchars(stripslashes($q)) . " returned x results.";
Is this the correct way to do it?
By the way, I know that PDO and mySQLi is preferred as they sanitize themselves through the use of prepared statements, but I have no real experience with them whatsoever. But I would gladly take a look, if you guys could link me some newbie tutorials/explanations.
Furthermore, I heard that magic_quotes and charset could in some way or another lead to injections -- is this correct?
For some reason we need also escape a backslash too.
So, the proper code would be, I believe
if(isset($_GET['q'])){
$_GET['q'] = trim($_GET['q']);
if(strlen($_GET['q']) >= 2){
$q = $_GET['q'];
$q = '%'.addCslashes($q, '\%_').'%';
// now we have the value ready either for escaping or binding
$q = mysql_real_escape_string($q);
$sql = "SELECT name, age, address FROM book WHERE name LIKE '$q'";
//or
$sql = "SELECT name, age, address FROM book WHERE name LIKE ?";
$stm = $pdo->prepare($sql);
$stm->execute(array($q));
$data = $stm->fetchAll();
}
}
For the output, use
echo htmlspecialchars($_GET['q']);
stripslashes not needed here.
Furthermore, I heard that magic_quotes and charset could in some way or another lead to injections -- is this correct?
magic quotes won't harm your security if you won't use them.
charset is dangerous in case of some extremely rare encodings but only if improperly set. if mysql(i)_set_charset or DSN (in case of PDO) were used for the purpose - you are safe again.
As for PDO, a tag wiki should be enough for starter, I believe

What is the use of preg_match('/(benchmark|sleep)/i', $id)

I today i start to read different articles about SQLi and DoS/DdoS to know how to protect my site and i found this thing :
Link: link to the article
// DB connection
// $id = (int)$_GET['id'];
$id = $_GET['id'];
$result = mysql_query("SELECT id,name,pass FROM users WHERE id = $id")
or die("Error");
if($data = mysql_fetch_array($result))
$_SESSION['name'] = $data['name'];
if(preg_match('/(benchmark|sleep)/i', $id))
exit('attack'); // no timing
I want to know the use of this.Also after this the guy show how to bypass it and i want to know if PDO is secury?
if(preg_match('/(benchmark|sleep)/i', $id)) checks if the $id matches the strings benchmark or sleep (the i stands for case-insensitive).
In the context it's presented I'd say this makes no sense what so ever though... I'd rather do this, and be done with it:
$id = (int) $_GET['id'];
$result = mysql_query('SELECT id,name,pass FROM users WHERE id = '.$id);
Notice I cast the id to an int, so if it's anything else it should just end up being 0, which most likely doesn't match anything since id columns usually starts on 1 (from my experience anyways).
I want to know the use of this
That's quite silly and apparently useless attempt to detect a possible SQL injection which is supposed to run a resource-consuming query.
Also after this the guy show how to bypass it
No wonder.
Once you have a code open to injection, thaere are thousands methods to run it.
The only your concern should be injection in general.
Once you protected - no ddos injection would be possible.
i want to know if PDO is secury?
First, it is not PDO secure, but strict and constant use of prepared statements considered secure.
Second, nope, prepared statements helps only half the problem

Sample code to fix this particular SQL-injection hole

Please read fully first
In this answer: How to prevent SQL injection with dynamic tablenames?
Pekka points out why this code:
$clas=$_POST['clas'];
$query="SELECT * FROM $clas ";
Cannot be repaired by using a PDO or mysql-real_escape_string().
Can anyone please provide sample code how to fix this so a newbie can paste that code
(after/adjusting it to his needs) and be safe from SQL-injection.
Please don't explain SQL-injection, I know all about injection and PDO, I just need sample code
You could use a whitelist to ensure that the value is indeed one of the tables you wish to be accessed in that way.
Example:
$allowed_tables = array('table1', 'table2');
$clas = $_POST['clas'];
if (in_array($clas, $allowed_tables)) {
$query = "SELECT * FROM `$clas`";
}
Note that constructing SQL queries directly from GET or POST parameters is usually a bad idea anyways, but a whitelist can make it safe.
You can use the for escape :P
$clas = str_replace('`','\\`',$_POST['clas']);
$query = "SELECT * FROM \`{$clas}\`";
So, is a ver very bad idea.
Do it different.

Categories