I had a quick question with regard to prepared statements within PHP. I was previously using the mysql_query function to manipulate database data, but was told that for security issues I should consider using prepared statements. I have made the transition, but I have a few questions on how to detect whether a query has failed.
Below I have a piece of example code. The $con variable is a connection which is specific depending on the query I am attempting, in this case the connection would be to my database through an account with only select permissions.
$stmt = $con->stmt_init();
$stmt->prepare("SELECT COUNT(*) FROM users WHERE username=?");
$stmt->bind_param('s', $username);
$stmt->execute();
$stmt->bind_result($user_count);
$stmt->fetch();
$stmt->close();
I was wondering how one can detect failure within any of these steps? The most simple solution I can imagine would be simply to wrap the code using a try/catch.. but I was wondering if there is a more sophisticated way of doing this.
Thanks for reading my question.
To expand on Jared's comment, you could do the following:
$stmt->execute();
if( !$stmt->errorCode() ){
// do something with results
}else{
// do something with the error
}
$stmt->close();
Related
I am in the process of migrating to php7 & msqli. I have a lot of old php files that will need prepared statements using bind_result & fetch. Thus, before I modify all these files I want to be sure I am coding prepared statements properly using bind_result & fetch, such that they are reasonably safe from sql injection. The code in my example works for me (binds & fetches properly), but I just want to be sure I coded them safely. I am still learning to code prepared statements for other implementations as well.
I also tried get_result instead of bind_result, but for my purposes (db interactions) I believe bind_result will suffice.
Here is the example of a php file that I will be using as the template for all my other php files that will need to be modified with prepared statements using bind_result & fetch:
<?php
//mysqli object oriented - bind_result prepared statement
//connect to database
require 'con_db.php';
//prepare, bind_result and fetch
$stmt = $con->prepare("SELECT image, caption FROM tblimages
WHERE tblimages.catID = 6 ORDER by imageID");
$stmt->execute();
$stmt->bind_result($image, $caption);
while ($stmt->fetch()) {
echo "{$image} <br> {$caption} <br> <br>";
}
$stmt->close();
//close connection
mysqli_close($con);
?>
And here is the php file that makes the db connection via "require", i.e. con_db.php:
<?php
//mysqli object oriented connect to db
//MySQL errors get transferred into PHP exceptions in error log
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
//establish connection, any connection errors go to php.errors
$con = new mysqli('localhost','uid','pw',
'db');
?>
I'm hoping I coded the prepared statements in a reasonably secure fashion to prevent sql injection. But any comments or suggestions are welcome. Thank you.
UPDATE: I decided to show (below) an example of the current code I was going to modify with prepared statements (with the bind_result, fetch example above). So below is a representation of the majority of php/mysqli code that currently exists that lives in many php files that would need to be modified. It is the existing mysql SELECT statements that vary the most. However, based on the feedback I have received I believe since there are no variables being passed there is no reason to use prepared statements with binding. However, I DO have some forms that DO pass variables (GET & POST), and I will modify those php files using prepared statements (bind_param, bind_result & fetch). I hope that made sense :-) I just thought it would be more useful to show an example of the code I originally was planning to modify since I may not need to modify much of it based on feedback I have received here, plus what I have read since my original post (on my concern re: sql injection). But please feel free to correct me if I'm wrong. Thank you.
<?php
//mysqli object oriented - mysqli_query & mysqli_fetch_array
//connect to database
require 'con_db.php';
//query and fetch
$result = mysqli_query($con,"SELECT image, caption FROM
tblimages WHERE tblimages.catid = 1");
while($row = mysqli_fetch_array($result))
{
echo $row['image'];
echo "<br />";
echo $row['caption'];
echo "<br />";
}
mysqli_close($con);
?>
You don't actually need bind_result() and fetch().
With PHP7, almost certainly you will have get_result() that will give you a familiar resource-type variable from which you can get the familiar array.
$stmt = $con->prepare("SELECT image, caption FROM tblimages
WHERE catID = 6 ORDER by imageID");
$stmt->execute();
$res = $stmt->get_result();
while ($row = $res->fetch_assoc()) {
echo "{$row['image']} <br> {$row['caption']} <br> <br>";
}
so you can keep a lot of your old code intact.
A couple notes:
Like #Dharman said, you don't really need a prepare/bind/execute routine if no placeholder marks are used i the query.
Like #Dharman said, better try PDO instead, it is much easier to use.
That said, you can greatly reduce the overhead with a simple mysqli helper function. Instead of writing this monster of a code (let's pretend the id in the query is dynamical)
$sql = "SELECT image, caption FROM tblimages WHERE catID = ? ORDER by imageID";
$stmt = $con->prepare($sql);
$stmt->bind_param("s", $catId);
$stmt->execute();
$res = $stmt->get_result();
you can have it in just two lines:
$sql = "SELECT image, caption FROM tblimages WHERE catID = ? ORDER by imageID";
$res = mysqli_select($con, $sql, [$id]);
So this is my first attempt at converting some previously used code to prepared statements in order to prevent SQL injections. I'm using mysqli procedural, as follows,
Old query:
mysqli_query($con,"UPDATE ulogs SET invalid_hits = invalid_hits + 1 WHERE user_id = $link AND date = '$date'");
if(mysqli_affected_rows($con) < 1) {
mysqli_query($con,"INSERT INTO ulogs(user_id,date,invalid_hits,unique_hits,non_unique_hits,earned,ref_earned,bonus) VALUES ('$link','$date',1,0,0,0,0,0)");
}
Prepared query:
$q1 = "UPDATE ulogs SET invalid_hits = invalid_hits + 1 WHERE user_id =? AND date =?";
$qa1 = "INSERT INTO ulogs (user_id,date,invalid_hits,unique_hits,non_unique_hits,earned,ref_earned,bonus) VALUES (?,?,1,0,0,0,0,0)";
if ($stmt = mysqli_prepare($con, $q1)) {
mysqli_stmt_bind_param($stmt,"is", $link, $date);
mysqli_stmt_execute($stmt);
$ucheck = mysqli_stmt_affected_rows($stmt);
mysqli_stmt_close($stmt);
}
if ($ucheck < 1) {
if ($stmt = mysqli_prepare($con, $qa1)) {
mysqli_stmt_bind_param($stmt,"is", $link, $date);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
}
}
mysqli_close($con);
exit();
And many more which get triggered under different circumstances but basically the same UPDATE first, then INSERT if nothing was updated.
Apparently it worked as I based my queries on examples from php.net. Apparently...
Because, after about 1 hour later, Apache was returning 500 server errors with php timing out while waiting for data from the db. Whereas with non-prepared queries such thing never happened.
So question is: have I done something wrong here? $stmt was being closed every time, along with $con so I don't know what may have caused the db to hang. Note that the server load did not went up either. It was just php-fpm reaching max clients due processes waiting for the db.
While I still haven't found WHY my original code for prepared statements ended up crashing the DB few hours later, I did eventually find a SIMPLE and WORKING out-of-the-box alternative by using a popular class made especially for this purpose: https://github.com/colshrapnel/safemysql
This class is pretty straightforward so even a newbie like myself was able to implement it using the examples found on their Github page.
Special thanks goes out to #YourCommonSense for pointing me in the right direction.
This question already has an answer here:
How to prevent SQL Injection in Wordpress?
(1 answer)
Closed 6 years ago.
My website was recently got Hacked/Compromised. Via google I have learnt it is a victim of site injections. I believe I have cleaned and hopefully secured my website but I'm looking for ways to prevent it from ever happening again. I came across a code (see below) and wanted to know whether it will
1) work to prevent such attacks in the future? and
2) where should I add this code as my website is built in WordPress.
Any help or even better codes anyone can provide will be greatly appreciated, I'm new to programming.
Code:
<?php
if(isset($_REQUEST["id"])){
if(!is_int($_REQUEST["id"])){
//redirect this person back to homepage
} else {
$id_raw = trim(htmlentities($_REQUEST["id"]));
$id_secure = mysql_real_escape_string($id_raw);
$sql = "SELECT * FROM databasetable WHERE id='".$id_secure."'";
}
}
?>
PDO is an acronym for PHP Data Objects.
PDO is a lean, consistent way to access databases. This means developers can write portable code much easier. PDO is not an abstraction layer like PearDB. PDO is a more like a data access layer which uses a unified API (Application Programming Interface).
You basically have two options to achieve this:
Example:
$qry = $con->prepare('SELECT * FROM student WHERE name = :name');
$qry->execute(array('name' => $name));
foreach ($qry as $get) {
// do something with $get
}
Setting up database using PDO
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDBPDO";
A DSN is basically a string of options that tell PDO which driver to use, and the connection details... You can look up all the options here PDO MYSQL DSN.
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username,$password);
Note: If you get an error about character sets, make sure you add the charset parameter to the DSN. Adding the charset to the DSN is very important for security reasons, most examples you'll see around leave it out. MAKE SURE TO INCLUDE THE CHARSET!
You can also set some attributes after PDO construction with the setAttribute method:
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare("SELECT id, firstname, lastname FROM MyGuests");
$stmt->execute();
// set the resulting array to associative
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
foreach(new TableRows(new RecursiveArrayIterator($stmt->fetchAll())) as $k=>$v) {
echo $v;
}
The way injection type attacks work, is by somehow getting an interpreter (The database) to evaluate something, that should have been data, as if it was code. This is only possible if you mix code and data in the same medium (Eg. when you construct a query as a string).Parameterised queries work by sending the code and the data separately, so it would never be possible to find a hole in that.
SQL Injection is a type of vulnerability in applications that use an SQL database. The vulnerability arises when a user input is used in a SQL Statement.
$n = $_GET['user'];
$sql = "SELECT password FROM tbl_login WHERE name = '$n' ";
As you can see the value the user enters into the URL variable user will get assigned to the variable $n and then placed directly into the SQL statement. This means that is possible for the user to edit the SQL statement.
$name = "admin' OR 1=1 -- ";
$query = "SELECT password FROM tbl_login WHERE name = '$n' ";
The SQL database will then receive the SQL statement as the following:
SELECT password FROM tbl_login WHERE name = 'admin' OR 1=1 -- '
To prevent SQL injections we will have to use something called prepared statements which uses bound parameters. Prepared Statements do not combine variables with SQL strings, so it is not possible for an attacker to modify the SQL statement. Prepared Statements combine the variable with the compiled SQL statement, this means that the SQL and the variables are sent separately and the variables are just interpreted as strings, not part of the SQL statement.
Prepared Statements with mySQLi.
Using the methods in the steps below, you will not need to use any other SQL injection filtering techniques such as mysql_real_escape_string(). This is because with prepared statements it is not possible to do conventional SQL injection.
mySQLi SELECT Query.
$n = $_GET['user'];
// Prepare the statement
if ($sql = $mysqli->prepare("SELECT password FROM tbl_login WHERE name=?")) {
// Bind a variable to the parameter as a string.
$sql->bind_param("s", $n);
// Execute the statement.
$sql->execute();
// Get the variables from the query.
$sql->bind_result($pass);
// Fetch the data.
$sql->fetch();
// Close the prepared statement.
$sql->close();
}
You will need to understand this:
Nothing is 100% secure.
All you can do is increase your level of security, by
implementing different security measures like filtering user input
before querying databases, using prepared statements.
Using a secure connection for server interaction by encrypting
the data using SHA or MD5 or some other salt encryption.
Using captcha in your forms to filter out bot attacks.
As far as your above code is concerned :
it is just checking whether the request id is an integer or not.
It is filtering out the special characters and then running the
query.
I would like to suggest you to check the below link :
https://www.owasp.org/index.php/PHP_Top_5
It will give you an insight of how to implement security in an application.
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
I want to make my db as secure as possible, and one of the things I've learned is to use prepared statements...so that's what i'm doing.
I just need a confirmation to make sure the order of execution is ok.
Does the following make sense, or am I missing something?
$sql = 'SELECT ...';
$conn = # new mysqli($host, $user, $pwd, $db);
$stmt = $conn->stmt_init(); // initialize a prepared statement
$stmt->prepare($sql);
$stmt->bind_param('i', ...);
$stmt->bind_result(..., ..., ...);
$stmt->execute();
while ($stmt->fetch()) {
...
}
$stmt->free_result(); // free the database resources for other queries
$stmt->close(); // close statement
$conn->close(); //close the database connection
I would say that you don't need to call stmt_init() -- at least, it doesn't seem to be necessary, according to the example on the page of mysqli::prepare()
You might also want to check :
If the connection to the DB has been successfully established -- see the examples on the page of mysqli::__construct(), using mysqli::connect_error.
If the statement has successfully be prepared -- checking the return value of mysqli::prepare() before using it
If the execution of the prepared statement has been successful, testing the return value of mysqli_stmt::execute()
Two quibbles:
Don't suppress errors with #
Never assume a DB-related operation succeeded. Always check for errors cases, such as when you call ->prepare() - your SQL statement may have a syntax error, yet you blindly proceed as if everything went perfectly. Same goes for the actual query execution - your query may be 100% syntactically correct, but fail due to external reasons.