SQL Injection via url

Asked

Viewed 1,666 times

2

At the moment my site is as www.site.com.br/blablabla_id_2 for the definition of pages.

I use mysqli to do database searches with this id.

$id = $_GET['id'];

$result = mysqli_query($con,"SELECT * FROM post WHERE id_post = '$id'");

if(mysqli_num_rows($result) == 0){

    header("Location: 404.php");

}else{

    //Resto do código

}

I will have some problem with sql Injection?

  • 9

    Yes you will have problem, do not just change the driver mysql_* by mysqli you need to handle the form entries and use Prepared statements. Here has other information about sql Injection

  • 2

    A good practice is to take your code in a "sandbox" environment and try to hack it. See if you can do something like try to access the URL "www.site.com.br/blablabla_id_2'-DROP TABLE (put the name of a table here)"

  • Thanks. I tested it in my code and the drop table didn’t work, as did other functions. Thanks for the help, I will read the best practices not to regret later.

  • 2

    You don’t need anything fancy, just use $id = mysql_real_escape_string( $_GET['id'] ); and do this in all the parameters that already solve the problem of Injection. Then, to make the code more elegant, you can even use Prepared statements if you want, instead of Escaping. And if you don’t want to change databases, you better stay in mysqli anyway. No need to use intermediate layers and resource simulation like others libs fashion do. Remembering that Prepared statements were made for performance in loops, the protection is just a "toast".

  • @Marcio You’re right that you don’t solve in all cases, but in this question-specific query you do. Numerical parameters require another type of treatment.

  • @Marciosimao ok, I typed wrong, it was supposed to be mysqli_ :) But I liked the link, because it also shows that PDO does not escape the problem nor with Prepared statements, because they are emulated and will suffer the Injection of any way, good to alert people who think it is the best thing in the world. At least we can reach a consensus: mysqli + Prepared statements is the safest way.

  • 1

    For those who don’t want to read the entire Q&A that @Marciosimao recommended, follow the summary: Pro mysqli_real_escape_string() be safe, remember to use mysqli_set_charset() before, setting for one of these 3 parameters: utf8, latin1 or ascii, otherwise some special characters can "leak" and still allow some attack (although the pointed bug is for mysql and not mysqli, but it costs nothing to do right).

  • @bfavaretto, The problem is that an application, however small it is, will hardly turn around just a query, so it is better to adopt techniques that close as many gaps as possible.

  • @Bacco, For Mysql users is a nice way, but it is good to always seek more comprehensive solutions. If tomorrow it will work with Postgresql, Oracle, SQL Server or any other database, I believe that the key is never to trust the inputs, for this PHP already provides excellent sanitizers, combining this to the use of PDO, the application will get a good degree of security. PDO also has the advantage of abstracting communication with the bank, so for any bank, the methods are the same.

  • 1

    I read the indicated Q&A, and what it says there is the opposite. PDO simulates the Prepared statements, therefore with PDO you have the bug with and without PS. Other than that, I can’t speak for others, but I particularly prefer to use native layer than these compatibility masks (just like using Jquery for something that solves with JS, for example). But I understand perfectly well that everyone knows what they do best with their experience. I know I’ll probably never need a PDO for anything, but I respect who you prefer. I just can’t imagine that this whole switching bank thing is always normal.

  • @Bacco, My interpretation was different, I did not see there opposite of what I am saying, in fact what I said is something universal. About PS, this cannot be called a bug, but rather vulnerability. Also, it is quite simple to activate the Prepared statements natively in the PDO. About the bank, I never talked about changing, what I said was that today it is possible that a good developer has to work with more than one bank, at that time the PDO will be a hand in the wheel.

Show 7 more comments

4 answers

4


Joining what I had already commented with Q&A pointed out by Marcio, follows a modification that solves the problem in the first two lines of code, saving an unnecessary application layer for the case in question:

mysqli_set_charset( 'utf8' );                   // pode usar 'latin1' ou 'ascii'
$id = mysqli_real_escape_string( $_GET['id'] ); // aqui estamos 100% sanitizados.

$result = mysqli_query($con,"SELECT * FROM post WHERE id_post = '$id'");
if(mysqli_num_rows($result) == 0){
    header("Location: 404.php");
}else{
    //Resto do código
}

Notes:

  • The first line is extremely important to avoid a vulnerability that allows certain characters to escape sanitation. Setting the correct page, the interpretation of the escape works as expected.

  • In the second line we do the sanitization itself.

  • Important to note that although you are working with numerical data, I have not removed the quotes from your query purposely, as an extra protection. If you prefer, you can remove them, but forcing something in the sense of $id be even numerical (summed with zero, for example).

  • 1

    I appreciate the collaboration of all. I think I’ll stick with this code that most resembles mine. Thanks! I hope you avoid even sql injections!

3

Your code is totally vulnerable to SQL Injection, to prevent intrusions you can use two very useful resources, the input filters and the PDO for your query to be parameterized.

Your code with the use of these resources becomes:

$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);

// Depois de criar o objeto PDO com o driver do seu banco de dados, utilize o código abaixo

$Ps = $Pdo->prepare('SELECT * FROM post WHERE id_post = :id');

$Ps->execute([':id', $id]);

if ($Ps->rowCount() == 0) {
    header('Location: 404.php');
    die();
} else {
    // Pelo menos um registro foi localizado
}

3

I would do as Rcio Simao proposed, using PDO, but if in your case it is complex change all the code from mysqli to PDO, follows below, an alternative:

    <?php 
    class DB{
        private $conn;

        public function getConnection(){
            $this->conn = new mysqli("localhost", "root", "", "mvc");
        }
        public function execReader($SQL){
            return $this->conn->query($SQL);
        }            
    }

    $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
    $SQL = "SELECT * FROM produtos WHERE id = ".  addslashes($id);

    $DB = new DB();
    $DB->getConnection();
    $query = $DB->execReader($SQL);
    if ($query == 0) {
        header('Location: 404.php');
        exit();
    }

    $vo = new ProdutoVO();
    while($reg = $query->fetch_array(MYSQLI_ASSOC)){
        $vo->setId($reg["id"]);
        $vo->setNome($reg["nome"]);
        $vo->setMarca($reg["marca"]);
        $vo->setPreco($reg["preco"]);
    }

    var_dump($vo);
    ?>
  • I took 3 blank lines of your code so it doesn’t appear scrollbar and fit everything on the screen at once. If you don’t think it looks good, feel free to reverse the edit.

  • That’s great Bacco! Thanks

2

Friend, to solve this problem and make your life easier regarding database queries among other various functions, try using "PHP PDO", an example that would solve your problem would be this:


prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);

// insert one row
$name = 'one';
$value = 1;
$stmt->execute();

// insert another row with different values
$name = 'two';
$value = 2;
$stmt->execute();
?>

Take a look at the documentation of Prepared Statements PHP and in that of PDO

Browser other questions tagged

You are not signed in. Login or sign up in order to post.