4
include "../admin/site.php"; // Setup db connection.

$appid = -1;
if (is_string($_GET["id"]))
{
    $id = mysql_real_escape_string($_GET["id"]);
    $sql = "select * from version where id=$id";
    $ver = mysql_query($sql);
    if ($id > 0 && $ver && mysql_num_rows($ver))
    {
        $appid = mysql_result($ver, 0, "AppID");
        $app = DLookUp("apps", "name", "id=$appid");
        $name = mysql_result($ver, 0, "Name");
        $notes = mysql_result($ver, 0, "Notes");
    }
    else $app = "No version by that ID";
}
else $app = "No ID";

/* some html snipped */

if (isset($app) && isset($name))
    echo $app . " v" . $name;
else
    echo "v###";

/* some html snipped */

if (isset($appid))
{
    $url = "/" . DLookUp("apps", "Page", "id=$appid");
    echo "<a href=\"$url\">Up</a> to $app...";
}
if (isset($notes))
    echo $notes;

Somehow this code is allowing someone to see the entire contents of my database. I would've thought that mysql_real_escape_string would prevent that sort of attack? I could cast $id to an integer which should fix the issue, but I want to understand what I did wrong here, so I don't keep repeating my mistake.

4
  • this is only a possible duplicate (haven't raised a flag)- stackoverflow.com/questions/1220182/… Commented Dec 7, 2011 at 2:22
  • 1
    shouldn't you put quotes around the $id? - $sql = "select * from version where id='$id'"; Commented Dec 7, 2011 at 2:25
  • 1
    Not putting this as an answer because I might be wrong, but what if someone passes id as 1 OR 1=1 Commented Dec 7, 2011 at 2:26
  • Good question, solicited numerous, production worthy responses. +1 Commented Dec 7, 2011 at 3:35

5 Answers 5

5

I think part of the problem is that you aren't using quotes around $id, so an attacker may send a value for id that is 1 OR 1=1 and the SQL executed would be:

select * from version where id=1 OR 1=1

mysql_real_escape_string() escapes just NULLs, newlines, and quotes (\x00, \n, \r, \, ', " and \x1a), so it is of little help if you don't quote the variable.

Sign up to request clarification or add additional context in comments.

4 Comments

This is true. Either make sure id is an int, or put quotes around it.
I think this is it. I've confirmed with at least one sql injection testing tool that putting quotes around the $id does indeed fix the flaw. Now to go and check about 200 other php pages :[
@fret: Putting quotes around ints is not the best approach, imo you should do something like $id = intval($id);
@elijunior isnt PDO and Prepared statements the best approach? Why use this old method when php5 is such a common place?
4

You (at least) need to quote the $id even the column is an integer

select * from version where id="{$id}"

But, if the supply $_GET["id"] using

$id = "xxx\" OR 1=1";
select * from version where id="{$id}" <-- this become a vulnerable 
as it eval to
select * from version where id="xxx" OR 1=1

You should consider bind the parameter :

select * from version where id :=id

Or at least doing the casting to integer $id = (int)$_GET["id"],
you are suppose to compare to integer column, aren't you?
(force it to zero when the $_GET["id"] is not an integer)

Take a look on this :-

And always enclose the database link identifier when dealing with mysql_* function
(you might have multiple database connections in a single page)

2 Comments

The casting to int is definitely a good way to go when you can. I'll keep that in mind, thanks.
@fret hi, bind parameter is proper way, and keep in mind the casting only works for integer/numeric column
1

It's possible for mysql_real_escape_string to fail to account for certain multi-byte encoded characters. Your attacker is likely using a multi-byte sequence that is improperly escaped for your db's particular charset.

The best way to prevent injection is through the use of prepared statements.

Comments

1

My take on this is that there are multiple possibilities:

As there are no quotes around $id an attacker may send a value for id that is 1 OR 1=1 and the SQL executed would be:

select * from version where id=1 OR 1=1

Check if its an Integer:

if (intval($_GET['id']) > [some condition]) //do something

Type Caste it:

$id = (int) $_GET['id'];

Lastly but not the least, I would use prepared statements

<?php
$query = "select * from version where id=?";
$stmt = $dbh->prepare($query);
$stmt->execute(array($_GET['id']);
?>

Comments

0

The only problem i could see is with your ID

I would check if its really an integer.

if (is_int($_GET['id'])) 
{

You can proceed... That will assure you that the id is really a integer.

3 Comments

$_GET data will always be a string type.
you have to caste it before getting an integer value out of $_GET values as its always an integer, But if you do caste it the statement is always true, so its an irony and this answer cannot be true
is_numeric() would still result to true, though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.