Mysqli prepared statement (SQL injection prevention) - php

after stopping the use of deprecated mysql_* functions, I switched to mysqli. But then, I noticed that non-prepared statements are non-secure against SQL injection. Then, I changed again my code.
What I had was the following function that checks if the variable $ID exists in the database and prints the value of title for that row:
function showPostTitle($ID, $mysqli) {
$result = $mysqli -> query("SELECT ID, title FROM blog where ID = $ID");
$row = $result -> fetch_array(MYSQLI_BOTH);
echo $row['title'];
}
I changed it to this:
function showPostTitle($ID, $mysqli) {
$stmt = $mysqli -> prepare("SELECT ID, title FROM blog WHERE ID = ?");
$stmt -> bind_param("i", $ID);
$stmt -> execute();
$stmt -> bind_result($ID, $title);
$stmt -> fetch();
print_r($title);
$stmt -> free_result();
}
My question is: is this the correct way to implement prepared statements? Plus, am I safe now from SQL Injections? Big thanks to whoever will answer this question :)

Your mysqli logic seems fine, there are some examples in the PHP manual here in case you have not seen them.
Why are you selecting the ID when not consuming it though? Also you don't really need to bind a result when it's only going to have one row returned in the full result set as I assume will happen in this case (ID is unique index in the table), use get_result instead.
Using mysqli prepare will protect against all the common injection attacks but not 0-day style stuff which hasn't made it to the driver yet.

Take a look at this post:
Are PDO prepared statements sufficient to prevent SQL injection?
It's using PDO instead of MySQLi, which is solving the same problem by creating prepared statements.
Sorry for not answering your question, but just wanted to provide a resource for you to consider.

Related

PHP Prepared Statement to delete all records

How do you delete all contents of a table in PHP MySQL using prepared statement? Do I have to use prepared statement to be safe or does using $deleteall = mysqli_query($conn,"DELETE FROM mytable;"); works with no difference?
Is this how it's supposed to be done:
$stmt = $conn->prepare("DELETE FROM mytable");
$stmt->execute();
There's no point in using prepared statements when you don't have parameters.
Prepared statements exist to make if efficient to execute a statement multiple times, possibly with different arguments. They also help with SQL injection prevention: you don't have to remember to apply quoting and escaping manually. But if you're going to run something only once there are no parameters, don't bother with prepared statements.
You may want to delete records with criteria parameters so the best way to do it:
$sqlQuery = "DELETE FROM TABLE_NAME WHERE id=?";
$statement = $conn->prepare($sqlQuery);
$statement->bind_param("i", $memberId);
$statement->execute();
Use this way, it is working fine on me. Hope it will help you.
$stmt= 'DELETE from mytable';
$result = $conn->prepare($stmt);
$result->execute();

How to use Functions to create Prepared Statements

I am new to these prepared statements (all my code so far has been procedural), and I was trying to create a function for executing prepared statements so I could save time writing a statement for every SQL query I need.
function executeQuery($stmt,$mysqli,$sql,$type,$param1,$param2,$param3){
$stmt = $mysqli -> stmt_init();
$stmt = $mysqli -> prepare($sql);
$stmt->bind_param($type,$param1,$param2,$param3);
$stmt->execute();
$stmt->close();
}
As you can see, my issue is that I would need a different function for prepared statements that only deal with 1 parameter in the query, one for 2 parameters, another for 3 and so on. I thought of making $param an array but I don't think that quite works.
Any ideas?

Is this kind of PDO query protected against SQL injection? [duplicate]

This question already has answers here:
How can prepared statements protect from SQL injection attacks?
(10 answers)
Closed 9 years ago.
First of all, yes, I've seen this Are PDO prepared statements sufficient to prevent SQL injection?, but it's not enough to answer my question, as I'm doing it differently.
Let's say I have code like this:
$userid = $_SESSION['data']['id'];
$query = "SELECT * FROM table WHERE userid='$userid'";
$action = $db->prepare($query);
$action->execute();
It's technically safe, as user can't affect to $userid, right? Say if I'm wrong on this.
But what if the code looks like this:
$userid = $_GET['id'];
$query = "SELECT * FROM table WHERE userid='$userid'";
$action = $db->prepare($query);
$action->execute();
Is it safe anymore? I'm unable to find any documentation about this that gives me black on white.
even if you have used PDO, your code is still vulnerable with SQL INjection because you have not parameterized the query, query must be parameterized in order for the values to be cleaned.
$userid = $_GET['id'];
$query = "SELECT * FROM table WHERE userid=?";
$db->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
$action = $db->prepare($query);
$action->bindParam(1, $userid);
$action->execute();
The second statement isn't safe.
Instead, you should do something like
$stmt = $db->prepare('SELECT * FROM table WHERE userid=:id');
$stmt->bindParam(':id', $userid);
$stmt->execute();
Source
It's technically safe, as user can't affect to $userid, right? Say if I'm wrong on this.
You are wrong with that. Session data is outside data and must be treated with caution. This is because of:
SessionID and SessionName are given with the request directly. These values can be easily manipulated so that some different data is being put into the memory of your application.
Persistence. Session data can be modified in the persistence layer so it qualifies as input data always (!).
You are likely expecting an integer value, so make it one:
$userid = (int) $_SESSION['data']['id'];
Especially as you directly substitute the variable into your SQL query.
In the future don't think if it is safe. Consider to do things in a safe manner so that even if you missed something in another layer (like input through session) don't breaks the data-flow in your application.
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
...
$userid = (int) $_SESSION['data']['id'];
...
$query = "SELECT column FROM table WHERE userid = ?";
$stmt = $pdo->prepare($query);
$stmt->bindParam(1, $userid);
$stmt->execute();

Prepared MySQLi statements

I've always used PDO statements, but for some reason I can't persuade the server guy to install PDO for php, but I do have MySQLi, I have no clue what I'm doing wrong, I do not get a connection error and I do not get a query error no matter how I try to output one. Here's what I'm doing.
include 'MySQLiConnect.php';
if($stmt = $mysqli->prepare("SELECT * FROM zipCodeTable WHERE zip_code = ?")){
$stmt->bind_param("s", '07110');
$stmt->execute();
$stmt->bind_result($resultsArray);
$stmt->fetch();
foreach($resultsArray as $columnData){
$matchingZipcode = $columnData['zip_code'];
$matchingTimezone = $columnData['time_zone'];
}
$stmt->close();
}
echo $matchingZipcode.', '.$matchingTimezone;
This is basically just to confirm a users zipcode, never used MySQLi prepared statements before, I tryed to do it straight from the manual, not sure what I'm doing wrong. Thank you for taking the time to read this.
You're trying to "bind" a literal string. You can't do this. You must bind a variable.
Change
$stmt->bind_param("s", '07110');
To
$string = '07110';
$stmt->bind_param("s", $string);
Also, when you bind a result you must provide a variable for each field returned.
For example:
$stmt->bind_result($zipCode, $timeZone);
This is slightly problematic when using SELECT *. You might be interested in checking out this comment for how you might want to go about it: http://www.php.net/manual/en/mysqli-stmt.bind-result.php#85470

multi query with prepared statements

I am trying understand how multi queries work in mysqli. But I confess that is not easy to understand.
Basically how I can do these queries in a multi query? The page doesn't talk about prepared statements in multi queries.
($sql = $db -> prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)"));
$sql -> bind_param('sss', $name, $email, $hash);
$sql -> execute();
($sq = $db -> prepare("INSERT INTO activation_link_password_reset (activation_link) VALUES (?)"));
$sq -> bind_param('s', $linkHash);
$sq -> execute();
You can't use prepared statements there, and the speedup is also negligible, so go for the easier to debug seperate queries.
If you really want to do it in 1 call with prepared statements, create a PROCEDURE (even more difficult to debug...), and prepare CALL(:param1,:param2);.
I actually just figured this out a few days ago. The move to MySQLi is not a trivial one but ill go ahead and show you the query loop for prepared statements.
$db = new mysqli(...)
$statement = $db->prepare('SELECT count(*) as [hitchhikers] FROM `T_Users` WHERE `id` > ? AND `signin` like ?');
$statement->bind_param("ss", 42, '%adams');
if ($statement->execute())
{
$statement->store_result();
$statement->bind_result($hitchhikers);
$statement->fetch();
// do something with hitchhikers here
}
else
{
// there was a problem with the execution, check $db->error
}
$statement->close();
$db->next_result();
Everything here works similar to the non oo format by replacing -> with _ and passing the returned value into the new function, but this process here is pretty much what you should look to be doing with each of those statements. You can abstract it away with a mysqli wrapper class as well.
After the $db->next_result() call you are able to repeat the process again. This change has to do with the way the MySQL connection and results are handled with MySQLi. You can, of course, avoid this issue entirely by switching back to mysql_ queries and resources.
Specifically for multi_query, you separate your queries with a semicolon, in a single string. The example you linked to shows how to process the results.
MySQLi Book #PHP.net

Categories