PDO and Escaping Input: Is this the safest way? - php

I wanting to check myself before I go live. I read so many different things on the internet but I want to know if this will absolutely protect my code for SQL Injection. If not, what do I need to add or take away?
$idtoapprove = mysql_real_escape_string($_POST['idtoapprove']);
$getcity = $conn->prepare('SELECT city, state FROM needs WHERE ID=:idtoapprove');
$getcity->bindParam(':idtoapprove', $idtoapprove);
$getcity->execute();
$cityrow = $getcity->fetch();
$needcity = $cityrow['city'];
$needstate = $cityrow['state'];
echo "$needcity, $needstate";

No need for mysql_real_escape_string here, actually, it's flat-out wrong (it's from a different, deprecated database library) and can damage your data. (Also, it would be ineffective here anyway - mysql_real_escape_string() is for escaping strings, it is useless for integers.)
The PDO prepared statement is enough.

Related

Effective protection function against SQL injection

I found this sanitizing function in a free software:
function VerifChamps($valeur)
{
$verif = (get_magic_quotes_gpc()) ? htmlentities($valeur, ENT_QUOTES) : addslashes($valeur);
return $verif;
}
The query is then done like this:
$login=VerifChamps($_POST['name']);
mysql_select_db(..., ...);
$query = sprintf("SELECT * FROM table WHERE login='%s'", $login);
$Result = mysql_query($query, $connexion) or die(mysql_error());
$row_RsProf = mysql_fetch_assoc($Result);
mysql_free_result($Result);
How safe is this code? How is it possible to improve it to make it even more secure?
EDIT: the server is running PHP v5.2.13, with Magic Quotes turned on
The short answer is that it's not safe at all.
Here's what's wrong with it...
You're checking get_magic_quotes_gpc, which has been removed from PHP for years
You're using htmlentities to encode the string if magic quotes is on, but not if it's off (way to corrupt your data)
Why are you using htmlentities at all to send data to the database? It doesn't prevent sql injection at all.
addslashes doesn't take the client connection character encoding into account when escaping your data (which makes it very unsafe)
You're returning an undefined variable (i.e. NULL) making the entire function useless
Also, mysql was deprecated and has been removed from PHP 7. Use the newer MySQLi extension instead.
You can simply replace your entire function with the functionality provided by newer database APIs like MySQLi and PDO which offer prepared statements and parameterized queries, which are already proven to be reliable and secure. The code you're providing in your example here is clearly ancient and very insecure.
For many days, i am using mysqli_real_escape_string function. It's a good function to avoid sql injection.
And, please avoid mysql extension.This extension will be removed in the future. Instead, the MySQLi or PDO_MySQL extension should be used.
You want to use prepared statements
http://www.w3schools.com/php/php_mysql_prepared_statements.asp

get id from url security

I'm creating a basic blog and I'm using the following code.
It's collecting the id (always a number) from the url and before I use, I wondered if anyone could check the security of the code and let me know if its ok?
I really don't want any injections, etc, and I want to keep it as much secured as possible.
<?php
if(is_numeric($_GET['id']) && $_GET['id'] > 0){
include("connectionfile.php");
$ia = intval($_GET['id']);
$ib = mysql_real_escape_string($ia);
$ic = strip_tags($ib);
$qProfile = "SELECT * FROM #### WHERE id='$ic' ";
$rsProfile = mysql_query($qProfile);
$row = mysql_fetch_array($rsProfile);
extract($row);
$title = trim($title);
$post = trim($post);
$date = trim($date);
mysql_close();
}else{
echo 'hack error here';
}
?>
$ia = intval($_GET['id']);
$ib = mysql_real_escape_string($ia);
$ic = strip_tags($ib);
strip_tags is useless, because it is only relevant in an HTML context. Any one of the other two methods would be sufficient to prevent SQL injection. Generally, just use the appropriate escaping mechanism for the language you're dealing with. In this case you're dealing with SQL, so mysql_real_escape_string alone is fine. See The Great Escapism (Or: What You Need To Know To Work With Text Within Text) for a step-by-step approach to escaping.
Better yet, learn PDO with prepared statements instead of the deprecated mysql_ functions, which solves the issue of SQL injection much better.
Don't use mysql_ functions. They are deprecated. Use mysqli or
PDO.
Use parameterized queries
Don't use "extract" as it pollutes the local scope. There are rare cases where it's safe, usually internal to an ORM, where it's
within the object. This is dangerous otherwise as all forms of
nasty variable names could be introduced, especially with successful
SQL injection.
Do exception handling so that database errors do not break the page entirely, and in the case of a bad query somehow forced via SQL Injection, nothing is displayed to indicate that the query was broken.
Even after you do all the above, still make sure you use htmlentities() or otherwise validate the data is what you expect before you display.
This code is a mess ;-)
if statement can be simplified "if (($id = (int)$_GET['id']) > 0) {"
if you acknowledge my 1. point, then $ia, $ib and $ic can be deleted
don't trim() database data! data should be trimed before INSERT into database.
read what #FilmJ has answered you

Are mysql_real_escape_string() and mysql_escape_string() sufficient for app security?

Will mysql_real_rescape_string() be enough to protect me from hackers and SQL attacks? Asking because I heard that these don't help against all attack vectors? Looking for the advice of experts.
EDIT: Also, what about LIKE SQL attacks?
#Charles is extremely correct!
You put yourself at risk for multiple types of known SQL attacks, including, as you mentioned
SQL injection: Yes! Mysql_Escape_String probably STILL keeps you susceptible to SQL injections, depending on where you use PHP variables in your queries.
Consider this:
$sql = "SELECT number FROM PhoneNumbers " .
"WHERE " . mysql_real_escape_string($field) . " = " . mysql_real_escape_string($value);
Can that be securely and accurately escaped that way? NO! Why? because a hacker could very well still do this:
Repeat after me:
mysql_real_escape_string() is only meant to escape variable data, NOT table names, column names, and especially not LIMIT fields.
LIKE exploits: LIKE "$data%" where $data could be "%" which would return ALL records ... which can very well be a security exploit... just imagine a Lookup by last four digits of a credit card... OOPs! Now the hackers can potentially receive every credit card number in your system! (BTW: Storing full credit cards is hardly ever recommended!)
Charset Exploits: No matter what the haters say, Internet Explorer is still, in 2011, vulnerable to Character Set Exploits, and that's if you have designed your HTML page correctly, with the equivalent of <meta name="charset" value="UTF-8"/>! These attacks are VERY nasty as they give the hacker as much control as straight SQL injections: e.g. full.
Here's some example code to demonstrate all of this:
// Contains class DBConfig; database information.
require_once('../.dbcreds');
$dblink = mysql_connect(DBConfig::$host, DBConfig::$user, DBConfig::$pass);
mysql_select_db(DBConfig::$db);
//print_r($argv);
$sql = sprintf("SELECT url FROM GrabbedURLs WHERE %s LIKE '%s%%' LIMIT %s",
mysql_real_escape_string($argv[1]),
mysql_real_escape_string($argv[2]),
mysql_real_escape_string($argv[3]));
echo "SQL: $sql\n";
$qq = mysql_query($sql);
while (($data = mysql_fetch_array($qq)))
{
print_r($data);
}
Here's the results of this code when various inputs are passed:
$ php sql_exploits.php url http://www.reddit.com id
SQL generated: SELECT url FROM GrabbedURLs
WHERE url LIKE 'http://www.reddit.com%'
ORDER BY id;
Returns: Just URLs beginning w/ "http://www.reddit.com"
$ php sql_exploits.php url % id
SQL generated: SELECT url FROM GrabbedURLs
WHERE url LIKE '%%'
ORDER BY id;
Results: Returns every result Not what you programmed, ergo an exploit --
$ php sql_exploits.php 1=1
'http://www.reddit.com' id Results:
Returns every column and every result.
Then there are the REALLLY nasty LIMIT exploits:
$ php sql_exploits.php url
> 'http://www.reddit.com'
> "UNION SELECT name FROM CachedDomains"
Generated SQL: SELECT url FROM GrabbedURLs
WHERE url LIKE 'http://reddit.com%'
LIMIT 1
UNION
SELECT name FROM CachedDomains;
Returns: An entirely unexpected, potentially (probably) unauthorized query
from another, completely different table.
Whether you understand the SQL in the attacks or not is irrevelant. What this has demonstrated is that mysql_real_escape_string() is easily circumvented by even the most immature of hackers. That is because it is a REACTIVE defense mechism. It only fixes very limited and KNOWN exploits in the Database.
All escaping will NEVER be sufficient to secure databases. In fact, you can explicitly REACT to every KNOWN exploit and in the future, your code will most likely become vulnerable to attacks discovered in the future.
The proper, and only (really) , defense is a PROACTIVE one: Use Prepared Statements. Prepared statements are designed with special care so that ONLY valid and PROGRAMMED SQL is executed. This means that, when done correctly, the odds of unexpected SQL being able to be executed are drammatically reduced.
Theoretically, prepared statements that are implemented perfectly would be impervious to ALL attacks, known and unknown, as they are a SERVER SIDE technique, handled by the DATABASE SERVERS THEMSELVES and the libraries that interface with the programming language. Therefore, you're ALWAYS guaranteed to be protected against EVERY KNOWN HACK, at the bare minimum.
And it's less code:
$pdo = new PDO($dsn);
$column = 'url';
$value = 'http://www.stackoverflow.com/';
$limit = 1;
$validColumns = array('url', 'last_fetched');
// Make sure to validate whether $column is a valid search parameter.
// Default to 'id' if it's an invalid column.
if (!in_array($column, $validColumns) { $column = 'id'; }
$statement = $pdo->prepare('SELECT url FROM GrabbedURLs ' .
'WHERE ' . $column . '=? ' .
'LIMIT ' . intval($limit));
$statement->execute(array($value));
while (($data = $statement->fetch())) { }
Now that wasn't so hard was it? And it's forty-seven percent less code (195 chars (PDO) vs 375 chars (mysql_). That's what I call, "full of win".
EDIT: To address all the controversy this answer stirred up, allow me to reiterate what I have already said:
Using prepared statements allows one to harness the protective measures of
the SQL server itself, and therefore
you are protected from things that the
SQL server people know about. Because
of this extra level of protection, you
are far safer than by just using
escaping, no matter how thorough.
No!
Important update: After testing possible exploit code provided by Col. Shrapnel and reviewing MySQL versions 5.0.22, 5.0.45, 5.0.77, and 5.1.48, it seems that the GBK character set and possibly others combined with a MySQL version lower than 5.0.77 may leave your code vulnerable if you only use SET NAMES instead of using the specific mysql_set_charset/mysqli_set_charset functions. Because those were only added in PHP 5.2.x, the combination of old PHP and old MySQL can yield a potential SQL injection vulnerability, even if you thought you were safe and did everything correctly, by-the-book.
Without setting the character set in combination with mysql_real_escape_string, you may find yourself vulnerable to a specific character set exploit possible with older MySQL versions. More info on previous research.
If possible, use mysql_set_charset. SET NAMES ... is not enough to protect against this specific exploit if you are using an effected version of MySQL (prior to 5.0.22 5.0.77).
Yes. If you will not forget to:
Escape string data with mysql_real_rescape_string()
Cast numbers to numbers explicitly (ie: $id = (int)$_GET['id'];)
then you're protected.
I personally prefer prepared statements:
<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
if ($stmt->execute(array($_GET['name']))) {
while ($row = $stmt->fetch()) {
print_r($row);
}
}
?>
It would be pretty easy to overlook one or another specific variable that has been missed when using one of the *escape_string() functions, but if all your queries are prepared statements, then they are all fine, and use of interpolated variables will stand out like a sore thumb.
But this is far from sufficient to ensure you're not vulnerable to remote exploits: if you're passing around an &admin=1 with GET or POST requests to signify that someone is an admin, every one of your users could easily upgrade their privileges with two or three seconds of effort. Note that this problem isn't always this obvious :) but this is an easy way to explain the consequences of trusting user-supplied input too much.
You should look into using prepared statements/parameterized queries instead. The idea is that you give the database a query with placeholders. You then give the database your data, and tell it which placeholder to replace with said data, and the database makes sure that it's valid and doesn't allow it to overrun the placeholder (i.e. it can't end a current query and then add its own - a common attack).

Where should I do mysql_real_escape_string?

I have a quick question about mysql_real_escape_string. Where should I use it?
I have a *.php file with form that is redirecting it to itself, but that file is using another file that has class in it with function Add(params);
So should I escape strings when they are submitted?
$catName = mysql_real_escape_string($_POST['edtCatAddName']);
Or should I escape strings in my class?
$catName = mysql_real_escape_string($catName);
Or perhaps both these situations are wrong and I need to do something else? I've tried to escape just my query like this
$query = mysql_real_escape_string("INSERT INTO cat (catName, catDescr, catImg, catSubLevel, catSubID) VALUES ('$catName', '$catDescr', '$catImgURL', $catSubLevel, $catSubID)");
But it's not too good because this way my query won't go since catName and some other variables are string type and I need to add ' before and after them and these chars are escaped.
Any advice? I'm very new to this...
So if I use PDO then all I have to do is
$STH = $DBH->prepare("my raw, not escaped query");
$STH->execute();
and I can feel secure?
nowhere, you should use PDO prepared statements instead to protect you against SQL-injections.
When to use mysql_real_escape_string()
Actually mysql_real_escape_string() is used while sanitize a input from a user. So you should (at least) use it everywhere a user can input anything that goes into a query. It is also very suggested to use Prepared Statements.
What Prepared Statements are
Basically they are sql queries that are very safe.
Let's make an example.
SELECT UserName FROM user WHERE UserUID = X
Is a simple query. Let's say that the X is a variable that come from a $_GET input. Some users could add to X everything. Even a 1; and then start a new query. This technique is called SQL Injection.
Now with mysql_real_escape_string() you solve part of this problem, and it's quite safe. But Prepared statements tell the server that
SELECT UserName FROM user WHERE UserUID =
Is something like a static part, and then that X is a variable. In this way the server is kinda prepared to execute such a query, and nothing else, considering any input in X like an input. In this way you have not to worry about user inputs at all.
you can do:
$catName = mysql_real_escape_string($_POST['catName']);
or use mysql_real_escape_string() directly in your query.
For values which are expected to be a number (integer, float) - you can either use intval($var) for integers or floatval($var) for floats.
BUT:
never use mysql_real_escape_string() for the entire query - that's simply wrong ;-)
EDIT:
I forgot to mention: the best is to use PDO(PHP Data Objects) -> http://de.php.net/PDO
Don't. Use parameters in queries using mysqli or PDO.

PHP security, intval and htmlspecialchars

<?php
$id = intval($_GET['id']);
$sql = mysql_query("SELECT username FROM users WHERE id = $id");
$row = mysql_fetch_assoc($sql);
$user = htmlspecialchars($row['username']);
?>
<h1>User:<?php echo $user ?></h1>
Can you see any threats in the above code? Do I have to use htmlspecialchars on everything I output? And should i use is_numeric or intval to check so that the get is numeric?
I'm just building a minimal site. I'm just wondering if the above code is vulnerable to sql injection, xss?
Generally speaking mysql_real_escape_string() is preferred but since it's a number, intval() is OK. So yes, it looks OK from a security perspective.
One thing though, on many platforms, ints are limited to 32 bits so if you want to deal in numbers larger than ~2.1 billion then it won't work. Well, it won't work how you expect anyway.
These sorts of security precautions apply to any form of user input including cookies (something many people forget).
I would strongly recommend using PDO and prepared statements. While your statement above looks safe, you're going to have problems as soon as you do more complex queries.
Instead of puzzling over whether a particular query is safe, learn about prepared statements and you won't have to worry. Here is your example, re-written with PDO:
# Make a database connection
$db = new PDO('mysql:dbname=your_db;host=your_db_server', 'username',
'password');
# The placeholder (:id) will be replaced with the actual value
$sql = 'SELECT username FROM users WHERE id=:id';
# Prepare the statement
$stmt = $db->prepare($sql);
# Now replace the placeholder (:id) with the actual value. This
# is called "binding" the value. Note that you don't have to
# convert it or escape it when you do it this way.
$stmt->bindValue(':id', $id);
# Run the query
$stmt->execute();
# Get the results
$row = $stmt->fetch();
# Clean up
$stmt->closeCursor();
# Do your stuff
$user = htmlspecialchars($row['username']);
I've added a lot of comments; it's not as much code as it looks like. When you use bindValue, you never have to worry about SQL injection.
Well,
You are casting the received id to an int ; so no possible SQL injection here.
And the rest of the DB query is "hard-coded", so no problem there either.
If id was a string in DB, you'd have to use mysql_real_escape_string, but for an integer, intval is the right tool :-)
About the output, you are escaping data too (and, as you are outputting HTML, htmlspecialchars is OK) ; so no HTML/JS injection.
So, this short portion of code looks OK to me :-)
As a sidenote, if you are starting developping a new website, it is the moment or never to take a look at either mysqli (instead of mysql), and/or PDO ;-)
It would allow you to use functionnalities provided by recent versions of MySQL, like prepared statements, for instance -- which are a good way to protect yourself from SQL injection !

Categories