SQL injection: Is this line safe? - php

I have this line:
$sql = "UPDATE votes SET up=up+1 WHERE id='{$p}'";
Now from what I've read one way of sql injection is caused by not "closing" sql queries properly which allows hackers to add additional info.
So my question is, is that line safe as to me the up=up+1 has not been "closed" but if I set it like this up='up+1' which to me makes it "closed" it does not work.
row up type is int(11) if that makes any difference.
Update:
$p is sanitized with a function
function sanitize($foo) {
if(get_magic_quotes_gpc() == true) {
$foo = stripslashes($foo);
}
return mysql_real_escape_string(htmlspecialchars($foo));
}

The up=up+1 is not vulnerable because it does not accept user input via a variable. It is merely incrementing an INT value already in your database.
The WHERE id='{$p}' may be vulnerable, however, if the variable $p is not properly filtered and escaped, but as you added above, it has been sanitized by a function. Hopefully the sanitize function goes as far as checking the appropriate type of the variable (be it an int or string or whatever) and checks the appropriate bounds for it.
As always, the safest method is to make use of prepared statements and parameterized queries rather than pass any variables directly into a SQL string. Most of the database APIs available in PHP for the various RDBMS' support prepared statements. (Notably not the mysql_*() functions, however).

It is not safe, unless $p is properly escaped. Otherwise imagine...
$p = "foo'; DROP TABLE votes; SELECT '1"
Then you'd end up executing something like:
UPDATE votes SET up=up+1 WHERE id='foo'; DROP TABLE votes; SELECT '1';
That wouldn't be very pretty...

Related

Protect generic sql query statements

Any way to prevent malicious sql statements without using prepared statements and parameterized queries?
Example after simplify:
<?php
$con = mysqli_connect($_POST['db_server'], $_POST['db_user'],
$_POST['db_password'], $_POST['db_database']) or die(mysql_error());
$result = mysqli_query($con, $_POST['query_message']);
?>
Is it possible to check out the parameter $_POST['query_message'] is safe or not?
You should always build your queries within your code and then sanitise any variables you're going to use within them. NEVER pass the query or the database connection variables in via $_POST unless your user is querying the database via that form, in which case I'd recommend you just install phpMyAdmin.
As for sanitising your variables, if you really don't want to use PDO's prepared statements, you can sanitise incoming integers as follows:
$id = (isset($_POST['id']) ? (int)$_POST['id'] : null);
if ($id) {
$sql = "SELECT *
FROM `table`
WHERE `id` = {$id}";
}
And for strings use this:
$username = (isset($_POST['username']) ? mysqli_real_escape_string($con, $_POST['username']) : null);
if ($username) {
$sql = "SELECT *
FROM `table`
WHERE `username` = {$username}";
}
You can also call real_escape_string() directly on your $con object as follows:
$username = (isset($_POST['username']) ? $con->real_escape_string($con, $_POST['username']) : null);
However, as with #Shankar-Damodaran above, I highly suggest you do use PDO prepared statements to query your database.
Why you don't wanna use Prepared Statements ? That is really weird. I strongly suggest you should go for it.
You could make use of mysqli::real_escape_string for escaping quotes that is commonly used for SQL Injection Attacks.
Something like...
OOP Style
$message = $mysqli->real_escape_string($_POST['query_message']);
Procedural Style
$message = mysqli_real_escape_string($link,$_POST['query_message']);
other way is using:
htmlentities($query);
as an extra you could use preg_match() regular expressions to avoid
the inclusion of certain words (SELECT, DROP, UNION .......)
Example:
try{
$query = sprintf("SELECT * FROM users WHERE id=%d", mysqli_real_escape_string($id));
$query = htmlentities($query);
mysqli_query($query);
}catch(Exception $e){
echo('Sorry, this is an exceptional case');
}
There are real world cases where prepared statements are not an option.
For a simple example, a web page page where you can do a search on any number of any columns in the database table. SAy that table has 20 searchable columns. you would need a huge case statement that has all 20 single column queries, all 19+18+17+16+15+14+13+... 2 column queries, all possible 3 column queries... that's a LOT of code. much less to dynamically construct the where clause. That's what the OP means by prepared statements being less flexible.
Simply put, there is no generic case. If there was, php would have it already.
real_escape_string can be beaten. a common trick is to % code the character you are trying to escape so real_escape_string doesn't see it. then it gets passed to mysql, and decoded there. So additional sanitizing is still required. and when all characters used in injection are valid data, it's a PITA, because you can't trust real_escape_string to do it.
If you are expecting an integer, it's super easy.
$sanitized=(int)$unsanitized;
done.
If you are expecting a small text string, simply truncating the string will do the trick. does't matter that it's not sanitized if there's not enough room to hold your exploit
But there is no one size fits all generic function that can sanitize arbitrary data against sql injection yet. If you write one, expect it to get put into php. :)

UPDATE only provided fields in MySQL table using PHP

I have a user table with and id field and 10 other fields storing user details of various types that the user can change via various web forms. I want to have a PHP script that gets POSTed changed values for some subset of these fields, and UPDATEs only those fields that are received in the POST data. I'm finding this surprisingly difficult to do in a way that doesn't suck. We use mysqli for all database interaction in the rest of this application so mysqli-based solutions are strongly preferred.
The options I've considered and dismissed so far:
1) Run a separate UPDATE query for every field provided in the POST data - yuck, I don't want to hit the database up to 10 times for something that could be done in one query.
2) Have a dictionary mapping field names to the fields' data types, and iteratively construct the query by looping through the provided fields, checking whether they are text fields or not, calling mysqli_real_escape_string on the string fields and otherwise sanitizing the others (e.g. by type checking or sprintf with '%i' placeholders). - Yuck! I could probably safely do things this way if I was careful, but I don't want to make a habit of using this kind of approach because if I'm careless I'll leave myself open to SQL injection. Parameterized queries don't give me the potential to screw up dangerously, but this approach does. My ideal is to never concatenate any data into an SQL query manually and always rely upon parameterized queries; the database libraries of other languages, like Python, let me easily do this.
3) Use a parameterized query - this is my ideal for everything, since as long as I insert all externally-provided data into my query via the bind_param method of a mysqli statement object, I'm immune to SQL injection and don't have to worry about sanitization, but using parameterized queries seems to be impossible here. The trouble is that bind_param requires that the data be passed as variables, since all arguments after the first are passed by reference. I can reasonably elegantly iteratively construct a query with ? placeholders, and while I'm at it construct the string of types that gets passed as the first argument to bind_param ('ssiddsi' etc.), but then there's no way I can see to choose at runtime which of my 10 fields I pass to bind_params (unless I have a switch statement with 10^2 cases).
Is there some PHP language construct I'm missing (something similar to array unpacking) that will allow me to choose at runtime which variables to pass as arguments to bind_param? Or is there some other approach I haven't considered that will let me solve this simple problem cleanly and safely?
You can easily combine 2 and 3 by means of my SafeMySQL library.
The code will look like
$allowed = array('title','url','body','rating','term','type');
$data = $db->filterArray($_POST,$allowed);
$sql = "UPDATE table SET ?u WHERE id=?i";
$db->query($sql, $data, $_POST['id']);
note that $allowed array doesn't make all these fields necessarily updated - it just filters POST fields out. So, even $_POST with only id and url would be correctly updated.
Nevertheless, using prepared statements, although toilsome, also quite possible.
See the code below
public function update($data, $table, $where) {
$data_str = '' ;
foreach ($data as $column => $value) {
//append comma each time after first item
if (!empty($data_str)) $data_str .= ', ' ;
$data_str .= "$column = $value" ;
}
$sql = "UPDATE $table SET $data_str WHERE $where";
mysqli_query($sql) or die(mysqli_error());
return true;
}
$data is an array, in your case it's $_POST.
If you want to be more specific about the data to be saved from $_POST array, you can define an array of allowed columns. For example,
$allowed = array('id', 'username', 'email', 'password');
By doing this, you can filter your $_POST array and pass it to update() function.

Properly Escaping with MySQLI | query over prepared statements

I have read this:
will help you NOT against injection.
Beause escaping is just a string formatting facility, not injection preventer by any means.
Go figure.
However, escaping have something in common with prepared statements:
Them both doesn't guarantee you from injection if
you are using it only against notorious "user input", not as a strict rule for the building ANY query, despite of data source.
in case you need to insert not data but identifier or a keyword.
On the following Post: Are dynamic mysql queries with sql escaping just as secure as prepared statements?
So my question is that using:
$Var = "UserInput Data Possible SQL Injection";
$mysqli->real_escape_string($Var);
does not provide protection against SQL Injection?
I want to use $mysqli->query(); so I can use fetch_array(MYSQLI_ASSOC); Because to be frank, I have no idea how to fetch the results as an array after using a prepared statement.
So If I have this in my Database Connection:
$STD = new mysqli('localhost', 'root', 'xx', 'xx');
$STD->set_charset('utf8');
if ($STD->connect_error) {
die("Standard Access Has Been Revoked. Please Contact Administration");
}elseif (!$STD){
die ("Other problem With Connecting To Database, Please Contact Administration");
}
as stated in the manual for real_escape_string
http://php.net/manual/en/mysqli.real-escape-string.php
The above lists:
Caution
Security: the default character set
The character set must be set either at the server level, or with the API function mysqli_set_charset() for it to affect mysqli_real_escape_string(). See the concepts section on character sets for more information.
Which links to: http://php.net/manual/en/mysqli.set-charset.php
My overall question can split into three options, the first would be asking for a fetch_array() equlivant for prepared statements, which will provide full SQL injection prevention due to prepared statements sending data as raw.
The first question in this format follows:
I'm using a Query as:
$GetCompletedQuery = $STD->query("SELECT Status FROM UserCompletion WHERE `UserID`=' ". $STD->real_escape_string($_SESSION['UID']) ."'");
$GetCompletedArray = $GetCompletedQuery->fetch_array(MYSQLI_ASSOC);
Which returns:
Array ( [Status] => 1 )
But using prepared statements:
$GetCompletedQuery = $STD->prepare("SELECT Status FROM UserCompletion WHERE `UserID`=?");
$GetCompletedQuery->bind_param('i', $_SESSION['UID']);
$GetCompletedQuery->execute();
$GetCompletedArray = $GetCompletedQuery->fetch_row;
print_r($GetCompletedArray);
Which returns:
Fatal error: Call to a member function fetch_row() on a non-object in /var/www/New/API/Constants.php on line 17
The same appears when I try fetch_array() which I know cannot be used with prepared statements.
So what would be the option for using prepared statements?
Second Question
If I use My Usual Query as:
$GetCompletedQuery = $STD->query("SELECT Status FROM UserCompletion WHERE `UserID`=' ". $STD->real_escape_string($_SESSION['UID']) ."'");
which enabled me to use fetch_array(); is data properly secured from SQL injection?
Third Question:
Should I be escaping/protecting from SQL injection for a $_SESSION['UID']; as this is assigned in the following manor:
$InnerJoinQuery = $STD->query("
SELECT Users.ID, Users.Username, Users.Password, UserInformation.LastName, UserInformation.Firstname, UserInformation.DOB
FROM Users
INNER JOIN UserInformation
ON Users.ID = UserInformation.UserID WHERE Users.Username = '".$_SESSION['real_name']."'");
$InnerJoinArray = $InnerJoinQuery->fetch_array(MYSQLI_ASSOC);
$_SESSION['UID'] = $InnerJoinArray['ID'];
$_SESSION['Password'] = $InnerJoinArray['Password'];
$_SESSION['Firstname'] = $InnerJoinArray['Firstname'];
$_SESSION['LastName'] = $InnerJoinArray['LastName'];
$_SESSION['DOB'] = $InnerJoinArray['DOB'];
This snippet explained:
User Logs in with username & password, the file gets information from the database based on $_SESSION['real_name'];
and adds to the $_SESSION array with the results, adding each into a different key.
The question for this chunk is should I even be escaping/protecting from SQL injection when the $_SESSION['UID']; is assigned through the database based on $_SESSION['real_name'];
Thankyou for your time for reading over this massive chunk.
http://php.net/manual/en/mysqli-stmt.get-result.php
Yes, but it is very bad practice:
it will help you in this case but only in this case and deceive with anything else
manual escaping is just silly, better let driver to do it for you
YES, because there is no such thing like SQL injection but improper formatting ONLY
is that using $mysqli->real_escape_string($Var); does not provide protection against SQL Injection?
I didn't change my mind: sure, it doesn't.
It will do only if you enclose the resulting value in quotes (and set proper encoding using mysqli_set_charset() to be strict).
Look, SQL injection not something essential, existing on it's own, but it's rather mere a consequence. A consequence of improperly formatted query.
When creating a query, you have to properly format every part of it. Not because of whatever "injection" but for the sake of it. When you're going to insert a string into query, you HAVE to put it into quotes, or you will get a syntax error. When you're going to insert a string into query, you HAVE to escape these quotes were used to delimit this string, or you will get a syntax error. And so on. It is proper formatting that should be your concern, not scaring tales about injection. And as long as you have every dynamic query part properly formatted according to it's type - no injection ever could be possible
So, the source of variable or it's value should never be your concern. But only it's place in the query:
strings have to be enclosed in quotes and have these quotes escaped.
numbers have to be cast to it's type.
identifiers have to be enclosed in backticks and have these backticks doubled
When it's going for the static part of the query, hardcoded in the script, we don't use such strict standards - say, we're not enclosing every identifier in backticks.
But when it's going for the dynamical part of the query, applying formatting rules should be strict rule, as we cannot know variable content for sure.
By the way, there is another way to format your strings and numbers - prepared statements. It is not as convenient as it should be, but because it is using placeholders to represent your data in the query, it it recommended to use over silly manual formatting.

php PDO UPDATE statements: which is safer?

I'm learning PDO, and finding it tricky to make sure my statements work correctly. I have a PHP function which is updating my database by simply adding the number 1 to the total.
function add_rating($place_id,$rating_id) {
//make $db accessible inside the function
global $db;
// query v1
$sql = "UPDATE places_ratings SET ? +1 WHERE place_id=?";
$q = $db->prepare($sql);
$q->execute(array($rating_id,$place_id));
}
I tried variations of this, none of which I could get to work. I don't know if I was using question marks wrong. I was following this guide and also a previous SO question. In the end I tried a different method which worked first time, so I am tempted to re-use it as it also seems a lot simpler.
function add_rating($place_id,$rating_id) {
//make $db accessible inside the function
global $db;
// query v2
$query = "UPDATE places_ratings SET $rating_id = ($rating_id +1) WHERE place_id = $place_id";
$update = $db->query($query);
}
My question is: which statement is better/safer? And secondly, what am I doing wrong with the first version with question marks? Thanks...
In general prepared statements as in your first example are safer because they are immune to SQL injection.
Your example doesn't work because you can't specify field names using a ? parameter in a prepared statement. Even if you could your SQL still would be wrong, this would expand to
UPDATE places_ratings SET whatever +1 WHERE place_id=?
which is not valid.
If your $rating_id is generated in code and not taken from user input you could combine both approaches.
Prepared statements are not simply like copy'n'pasting variables into a piece of text. Prepared statements separate between the query logic and the values the query should work on. They're there so you're able to tell your database "You're supposed to do this", let the database understand it, then give it the values it's supposed to do that something with. The logic itself cannot be variable, it needs to be complete the first time.
Therefore, you can only use placeholders for values. Your query needs to read UPDATE ... SET FIELD = VALUE WHERE FIELD = VALUE. The FIELD parts need to be in the statement, the VALUE parts you can use placeholders for. It looks like your $rating_id variable is a variable field name. First of all, that's a bad idea. You should not make field names variable if possible. But if you have to, you cannot use prepared statement placeholders for them. Instead, you'll have to do it like this:
$rating_id = 'field_name';
$query = "UPDATE places_ratings SET `$rating_id` = `$rating_id` + 1 WHERE `place_id` = ?";
$stmt = $db->prepare($query);
$stmt->execute(array($place_id));
It's up to you to make sure $rating_id is safe and contains known, whitelisted values. Don't let the user supply the value for it in any way.
Please, go an learn what prepared statements are. And you could also use a tutorial, that does not promote bad practices and vulnerable code.
A correctly created and used prepared statement will always be more secure then concatenated query string, because prepared statements send query logic and data separately.
Also , if you are using PDO, then quite often the use of bindParam() method should be preferred over passing the values directly in the execute() method as an array. This is because, when passing values in execute(), the values are bound as PDO::PARAM_STR, even if DB column expects and integer.
P.S. Stop using global in your code !!

does PHP mysql_real_escape_string() protect database name?

I know that mysql_real_escape_string()
prepends backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a
I know how this protects a query from injection into something like a variable in a where clause. But here's a scenario I am unsure of:
$query = "SELECT * FROM $db WHERE 1";
If $db is taken from a user input, then the user could insert something like:
$db = 'RealDatabase WHERE 1; DELETE FROM RealDatabase WHERE 1; SELECT FROM RealDatabase';
From my understanding, mysql_real_escape_string() would not affect this string,
making the final query:
$query = "SELECT * FROM RealDatabase WHERE 1; DELETE FROM RealDatabase WHERE 1; SELECT FROM RealDatabase WHERE 1";
which would delete the database. Is there another level of protection I am unaware of?
The level of protection you are looking for is supplied by backticks:
"SELECT * FROM `$db` WHERE 1";
Backticks are used to qualify identifiers that could otherwise be ambiguous (ie. MySQL reserved words), and if you are accepting user input or have variably-named columns or databases, you absolutely should use backticks, or I can promise that you will run into trouble in the future. For example, what if you had a system where a temporary field name was created with some user input, only it turned out the field ended up being named update?
"SELECT field1,field2,update FROM table;"
It fails miserably. However:
"SELECT `field`,`field2`,`update` FROM table"
works just fine. (This is actually a real example from a system I worked on a few years ago that had this problem).
This solves your problem in terms of putting in bad SQL. For instance, the following query will simply return an "unknown column" error, where test; DROP TABLE test is the injected attack code:
"SELECT * FROM `test; DROP TABLE test`;"
Be careful though: SQL Injection is still possible with backticks!
For instance, if your $db variable contained data that had a backtick in it, you could still inject some SQL in the normal way. If you're using variable data for database and field names, you should strip it of all backticks before putting it into your statement, and then qualifying it with backticks once inside.
$db = str_replace('`','',$db);
$sql = "SELECT * FROM `$db` WHERE 1";
I utilize a database wrapper which has separate functions for sanitizing data and sanitizing database identifiers, and this is what the latter does :)
You should really look into binding your SQL queries.
This will protect you from basically all SQL injection. It boils down to this:
(taken from PHP.net)
$stmt = mssql_init('NewUserRecord');
// Bind the field names
mssql_bind($stmt, '#username', 'Kalle', SQLVARCHAR, false, false, 60);
// Execute
mssql_execute($stmt);
And PHP has support for binded queries on basically all databases. Oh and of course you should still sanitize all input & output(display).
More info:
- http://php.net/manual/en/function.mssql-bind.php
No, mysql_real_escape_string isn't going to help you here. The function is not context-sensitive (it can't be, because it doesn't HAVE any context), and this is a completely different threat model.
You need to go and validate that the table exists, without sending the user-inputted table name directly to the server. The best solution is to use a server-side array/look-up table containing the table names they are allowed to use. If they try to use something that's not in there, then don't let them.
If you really need ALL of the tables, then you can just ask the server "what tables do you have?" and run through it's output (optionally caching it for some period of time to prevent asking the server every time) - but chances are, eventually you'll have a table that you don't want then to poke around in, and then you need to use the array thing anyway, so just go ahead and do that.
Instead of inserting the database name in the get query you can make a separate table of database names and ids. Then append only the id to the query. Then you can look up the corresponding database name for that id and use that. You can then make sure that the id received is numeric (is_numeric) and you can also be certain that the user can only choose from the databases that are in your list.
(Additionally this will prevent users from finding out names of databases and possibly use them elsewhere in an SQL injection on your site.)
Using the first method you parse the database name before using it in your query and make sure it contains no spaces.
Since table names do not accept whitespace characters, just strip them out. That would make the above $DB RealDatabaseWHERE1;DELETEFROMRealDatabase..... Such would invalidate the query, but prevent the flaw.
If you want to prevent this kind of 'hackish' things, just do explode(' ', $db) then get the result array's [0]. That would get the first part (RealDatabase) and nothing else.
Its just best to use it any time that you have questionable data being used. If you are specifying the table yourself and there's no room for tampering, there's no need to escape it. If your users are deciding anything that could potentially get run as a query, escape it.
If you really really must use a get from the user (bad bad bad) for your database then use the following style of coding...
$realname = '';
switch ($_GET['dbname']){
case 'sometoken' : $realname = 'real_name'; break;
case 'sometoken1' : $realname = 'real_name1'; break;
case 'sometoken2' : $realname = 'real_name2'; break;
case 'sometoken3' : $realname = 'real_name3'; break;
case 'sometoken4' : $realname = 'real_name4'; break;
case default : die ('Cheeky!!!');
}
$query = "SELECT * FROM `{$realname}` WHERE 1";
or alternatively ...
$realname = $tablenames[$_GET['dbname']];
if (!$realname)
die ('Cheeky!!!');
Using these 2 ways or some similar coding will protect your input from unexpected values.
It also means the user never gets to see the real table or database names which they may be able to infer information from.
Make sure you check the content of $_GET['dbname'] to make sure it's valid first otherwise warnings will be issued.
I still say this is a very bad design, it is reminiscent of allowing users to provide a filename and passing that through to I/O functions without a check. It simply too unsafe to consider.
Security is too important to let laziness rule.

Categories