2

The best way to avoid SQL injection for defined value type such as numbers, is to validate the value; since it is easier to do so comparing with mysqli preparation. In PHP, we can do this by.

1. if(!is_numeric($value)) {$value=0;}
2. $value=floatval($value);
3. $value=intval($value);
4. $value=$value * 1;

What is the most reliable one? or a better idea?

UPDATE: Although I have stated in the original question, most of folks emphasized the usefulness of parameterized queries. Definitely, it is the most efficient way to avoid SQL injection. But when we can simply validate an integer numeric value; IMHO, there is no need to parametrization.

Googlebot
  • 15,159
  • 44
  • 133
  • 229
  • 3
    The best way to avoid SQL injection is to use parameterized SQL statements. – DOK Mar 12 '12 at 13:19
  • 1
    `$value=intval($value)`; Or `(int)$value`; – Vitalii Lebediev Mar 12 '12 at 13:19
  • 1
    Yes if you make everything right, there is no need for parametrization, and parametrization is not always possible as well. However you can choose any tool that does the job, `is_numeric` and `floatval` is not part of it if you look for integer values (!). – hakre Mar 12 '12 at 13:28
  • If string might contain leading whitespace that you wish to ignore, use `$value = (int)trim($value);` – ToolmakerSteve Jul 17 '20 at 20:23

6 Answers6

5

For integers and floats you can use this if you don't want to do a parameterised query.

$clean_number = (int)$value;

$clean_number = (float)$value;

These are actually casting the value as int and float, this is faster than intval() and floatval() for example because it does not suffer the function overhead.

MrCode
  • 63,975
  • 10
  • 90
  • 112
  • @Ali there is actually nothing nice in function overheads. And nothing "faster" as well. – Your Common Sense Mar 13 '12 at 09:27
  • Benchmark of `(int)` vs `intval()`: http://stackoverflow.com/questions/239136/fastest-way-to-convert-string-to-integer-in-php – MrCode Mar 13 '12 at 09:42
  • @YourCommonSense Ironically, your comment doesn't make sense, but thanks for the downvote. Tests show the cast is faster than `intval()`. – MrCode Mar 13 '12 at 10:05
  • 4
    These tests make no sense. They are a light year far away from the **real life.** Keep you playing in the imaginary sandbox. – Your Common Sense Mar 13 '12 at 10:08
  • @YourCommonSense if you could show me a 'real life' example where `intval($num)` is faster than `(int)$num`, I would gladly edit my answer. When you make claims like that, you should back it up by pointing to some sort of research so people can make up their own minds instead of trying to force your views on people. – MrCode Mar 14 '12 at 08:05
  • 2
    I haven't said that intval($num) is faster than (int)$num. They are equal. Take whatever real life script which is using int casting, create 2 version of it, (int) and intval() one, run apache benchmark on both versions. Try to find the difference. – Your Common Sense Mar 14 '12 at 08:38
  • If string might contain leading whitespace that you wish to ignore, use `$clean_number = (int)trim($value);` – ToolmakerSteve Jul 17 '20 at 20:23
4

I prefer to use the filter extension:

$id = filter_var($id, FILTER_SANITIZE_NUMBER_INT);

You should definitively go for parameterized queries.

jpic
  • 32,891
  • 5
  • 112
  • 113
  • Its worth mentioning pro/con of using sanitize filter. Pro: If there is a number in the input, it will find it. Con: Is that really what you want given a garbage string? - the more restricted behaviour of `(int)` (or `intval()`) is often more appropriate - unusual strings simply become `0`. HOWEVER, AFAIK, `(int)` doesn't `trim` whitespace, so the alternative to filter, when don't want to strip all garbage, probably should be `(int)trim($value);` – ToolmakerSteve Jul 17 '20 at 20:19
2

You can use intval() to coerce a value to integer. The most reliable way to avoid SQL injection is to use parameterised queries.

Hammerite
  • 21,755
  • 6
  • 70
  • 91
2
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';

try {
    $dbh = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}

$sth = $dbh->prepare('SELECT * FROM table WHERE id = :id');
$sth->bindParam(':id', $id, PDO::PARAM_INT); 
$sth->execute();
$result = $sth->fetchAll(PDO::FETCH_ASSOC);
cetver
  • 11,279
  • 5
  • 36
  • 56
  • Yes. Because you don't have dozens of different filtering functions to know. You create your SQL request then give it its params. – Arkh Mar 13 '12 at 09:59
  • 1
    @Arkh it is not about different filtering functions. it's about **this very code**. Which is a mile far away from being easy. – Your Common Sense Mar 13 '12 at 10:15
  • @Arkh if you want to talk of "different filtering functions", there are "dozen" different binding types as well. – Your Common Sense Mar 13 '12 at 10:15
  • 2
    The filtering part of this code is the prepare / bindparam lines. All other things are connecting to the db and getting the request result which you'll have to do even with the mysql functions and (int) casting. – Arkh Mar 13 '12 at 10:48
1

If you want a seasoned advise, you have to change your mind. Completely.

The thing that being your main concern, in fact, is the most negligible thing in the world. You may use one way or another with not a slightest difference. There is no "most reliable" way. That's just several ways of doing the same.

On the other hand, "there is no need to parametrization" is a grave delusion.
Parameterized queries can do any good only if used explicitly, throughout whole site, with no exceptions. One exception can spoil all the defense.

Not to mention that parameterized query can make your life much, much easier.
Prepared statements are not such an ugly code that propagated on this site by some enthusiasts. It's actually quick and neat way of writing safe code.
Say, the code that took cetver a dozen lines can be done in just one:

$data = $db->getAll("SELECT * FROM table WHERE id = :placeholder:",$id);

and be perfectly safe
without ugly manual binding.
without error-prone manual casting.

Another example to show you the power of placeholders

$sql = "SELECT * FROM table WHERE tstamp BETWEEN ?i AND ?i AND flag=?s AND IN in (?a)";
$data = $db->getAll($sql,$min,$max,$flag,$array_of_ids);

Two lines.

I am not too good with PDO but it would be like a dozen lines of code even without connect

$in  = implode(',', array_fill(0, count($array_of_ids), '?'));
$sql = "SELECT * FROM table WHERE tstamp BETWEEN ? AND ? AND flag=? AND id IN ($in)"
$sth = $dbh->prepare($sql);
$stmt->bindValue(1, $min);
$stmt->bindValue(2, $max);
$stmt->bindValue(3, $flag);
foreach ($array_of_ids as $i => $id) {
  $stmt->bindValue(($i+4), $id);
}
$sth->execute();
$result = $sth->fetchAll(PDO::FETCH_ASSOC);

And comparable amount with your current manual casting.

This is actually the power of programming.
One can write a program to do all the dirty job for them.
An approach almost never seen on this site.

Sapienti sat

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • very interesting approach to make prepared statement a constant culture of coding. I really appreciate your way to use prepare statement in one line code; but I wonder how to use it in PHP mysqli? – Googlebot Mar 13 '12 at 11:04
  • One have to invent a *type-hinted placeholder* for this. the query actually have to be like `"SELECT * FROM table WHERE id = ?i"`, where `?i` is a placeholder and `i` is a type. Some parsing have to be done to get the placeholders and their types from the query. After that all data can be easily binded/substituted according to the placeholder type. – Your Common Sense Mar 13 '12 at 11:41
  • Incidentally, note that the code you've replaced from cetver's example only replaces _four_ lines, not 12. You didn't bother writing the other code. – sarnold Mar 14 '12 at 22:58
  • @sarnold well spotted! That's the problem with answers on this site. They **never** encourage good code styling or a slightest abstraction, **never show a better approach!** Face the reality, **look at the questions** - the codes always contains the same ugly raw API function calls. Even connection code seldom separated! And it's real codes right from the production. You all have an excuse for yourself - "it's just a demo!" but you have no idea that this "demo" always copy/pasted and used as is. So, it's 13, not 4. – Your Common Sense Mar 15 '12 at 03:18
  • @sarnold here is a dozen for your pleasure ;) – Your Common Sense Mar 15 '12 at 04:12
  • @YourCommonSense - Thank you for removing the offensive line from your answer +1. – M.Babcock Mar 15 '12 at 04:25
  • @M.Babcock get your vote back. There is nothing offensive in that line. Try to be not too touchy about the things, never intended to be offensive. – Your Common Sense Mar 15 '12 at 04:37
  • @YourCommonSense - Whether you saw it or not, it spurred conversation on meta (hence the mass downvoting). The vote probably would have went to some undeserving schmuck anyway. Enjoy! – M.Babcock Mar 15 '12 at 04:39
  • I don't care about voting in general and mass downvoting in particular. For the meaning of the words I trust the books I read, not conversations on whatever metas. – Your Common Sense Mar 15 '12 at 04:42
  • @YourCommonSense - Perhaps, but you must care about sharing your vast knowledge if you took the time to write out such a complete and thorough answer. It really is a good answer. It's too bad fewer people will read it and take it seriously with a negative vote count. Certainly if that didn't matter to you, you wouldn't have answered in the first place. – M.Babcock Mar 15 '12 at 05:05
  • @M.Babcock there are several problems with this. First, the answer will never be read anyway. Take whatever question from 2011 - it seldom has more than 30-40 views, all from the time it has been written. It is abyss. Every answer sunks forever. So, nothing to care of too much. Next, even without all that mess, my answer had no more votes than whatever rubbish answer above. Thats another problem - a good answer never can be distinguished from the bad one by votes. That's core problems of the stackoverflow site, not mine. – Your Common Sense Mar 15 '12 at 05:55
  • @Your: Excellent! I agree with your sentiment _They never encourage good code styling or a slightest abstraction, never show a better approach!_ I know I'm guilty of this too, but I long ago saw you respond something along the lines of, _I'm not going to answer the question as asked, but instead the question that should have been asked_, and I've kept it to heart since then. – sarnold Mar 15 '12 at 21:10
  • @sarnold thanks for the understanding. I just rethinked the raw code though, it could be actually 2 times shorter, 6 lines only, if we just pass an array of values to the `execute()`. Just for sake of impartiality - I am not going to abandon my idea of abstraction :) – Your Common Sense Mar 16 '12 at 06:04
-1

You can use this function, mysql_real_escape_string() to espace special characters, but what you are doing its fine to protect an sqli attack.

user1252306
  • 87
  • 3
  • 15