Convert normal SQL statements to prepared statements - php

I am using PHP 7, MySQL. I had been coding for my project a long time and it now has thousands of MySQL queries. I was not aware of prepared statements then.
Now, to avoid SQL injection, I want to use prepared statements but it is difficult for me to convert all of them one by one to prepared statement.
Is there any way I could parse a normal statement and convert it to prepared statement automatically using PHP? For every MySQL query, I pass it to a PHP function before passing to MySQL command.
public function dbquery($query,$dbname,$dbclose="-1")
{
$this->mysqli->select_db($dbname);
$GLOBALS["dbr_total"]++;$GLOBALS["dbr_query"]++;
$colarr=Array();$tares=Array();
if ($result = $this->mysqli->query($query))
{
$GLOBALS["dbretry"]=0;
$finfo = $result->fetch_fields();
$c=0;
foreach ($finfo as $val)
{
$colarr[$c]=$val->name;//get all colum names in this array
$c++;
}
$co=0;
while($obj = $result->fetch_object())
{
for($k=0;$k<count($colarr);$k++)
{
$elem=$colarr[$k];
$tares[$co][$elem]=$obj->{$colarr[$k]};
}
$co++;
}
if($co==0)
{
$GLOBALS["dbretry"]=0;
if($dbclose!="-1"){$this->dbclose();}
return EMPTY_RESULT;
}
}
else
{
if($GLOBALS["dbretry"]>3)
{
$GLOBALS["dbretry"]=0;
$errmsg=$this->mysqli->error;
$errno=$this->mysqli->errno;
if($dbclose!="-1"){$this->dbclose();}
$errobj=new ta_errorhandle();
$errobj->senderror("OOPS! Could Not process your query!".$errmsg,$errno,"1");
}
else
{
$GLOBALS["dbretry"]++;
$this->dbquery($query,$dbname);
}
}
//QUERY DONE
if($dbclose!="-1"){$this->dbclose();$result->close();}
unset($obj);unset($finfo);unset($query);unset($result);unset($colarr);unset($c);unset($co);
return $tares;
}
public function dbinsert($query,$dbname,$dbclose="-1")
{
$this->mysqli->select_db($dbname);
$GLOBALS["dbr_total"]++;;$GLOBALS["dbr_insert"]++;
if (!$this->mysqli->query($query))
{
$errmsg=$this->mysqli->error;
$errno=$this->mysqli->errno;
die("<br><br>".$errmsg."<br><br>".$errno);
if($GLOBALS["dbretry"]>3)
{
$GLOBALS["dbretry"]=0;
$logobj=new ta_logs();
$logobj->store_templogs("PROBLEM EXECUTING QUERY:".$query." ON ".$dbname);
return $this->mysqli;
}
else
{
$GLOBALS["dbretry"]++;
$this->dbinsert($query,$dbname);
}
}
else
{
$GLOBALS["dbretry"]=0;
}
if($dbclose!="-1"){$this->dbclose();}
return SUCCESS;
}
Now what I do is call $dbobj->dbquery("my query","database name"); where $dbobj is an object for the class of these functions.
How do I convert these functions so that whatever query I receive as parameter is used and converted to prepared statements? I cant rewrite every query in my code. I have written more than 10,000+ queries already.

The problem is that you appear to pass the whole query as a string to your dbquery() method.
Prepared statements only protect against sql injection in case a value is passed as a parameter to the query, not if the value is already in the query.
Since your entire query is a single string, with no placeholders and no values passed separately, you cannot use the prepared statements to protect against sql injection without changing how you pass the queries and parameters to dbquery() method. Therefore, you need to rewrite your queries in case you want to take advantage of sql prevention feature of the prepared statements.

Related

How to prevent SQL Injection in parameters with CakePHP

How to prevent SQL Injection while fetching data from the database when using parameters received from the user input:
if(isset($_GET['cityval']) && $_GET['cityval'] !=''){
$city = $this->request->query('cityval');
$searching .= " and college_city in ($city) ";
} else {
$searching .= "";
}
if(isset($_GET['scholarship']) && $_GET['scholarship'] !=''){
$searching .= " and college_scholarship = '".$_GET['scholarship']."' ";
} else {
$searching .= "";
}
And my main query is below
$search = $this->Search->query("select * from colleges where college_id!='' and status='active' $searching order by $order desc limit $start, 10 ");
Don't use raw queries. Simply use the query builder CakePHP provides, and it will prevent injection for you. See the online CakePHP book for more information.
It is SUPER rare to need to use raw queries in CakePHP.
What you try to do is obviously to search by get parameters. There is a wonderful plugin that makes it pretty easy https://github.com/FriendsOfCake/search
It could be actually that easy with the plugin:
$query = $this->Colleges->find('search', [
'search' => $this->request->query
]);
$this->set('results', $this->Paginator->paginate($query));
The search params itself will be handled in the model layer, check the plugins documentation on that. And the framework will take care of sanitizing the input.
It seems that Cake ORM uses PDO:
Underneath the covers, the query builder uses PDO prepared statements
which protect against SQL injection attacks.
Reference: https://book.cakephp.org/3.0/en/orm/query-builder.html
Unfortunately the way you're creating the query is vulnerable as you're not using Cake ORM neither PDO prepared statements.
If you want to use raw queries you could do something like this to protect your code:
// Add this in the beginning of the file/code:
use Cake\Datasource\ConnectionManager;
// Replace connection_name with the name of your connection (maybe it's "default")
$connection = ConnectionManager::get('connection_name');
$bindList = [];
$city = $this->request->query('cityval');
// PDO as a limitation for the parameter markers. See in the comments below
$cityList = array_filter(explode(',', $city), function($item) {
return preg_match('/^\d+$/', $item);
});
$csvCity = implode(',', $cityList);
$scholarship = $this->request->query('scholarship');
if (!empty($csvCity)) {
$searching .= " and college_city in ($csvCity)";
}
if (!empty($scholarship)) {
$searching .= " and college_scholarship = :scholarship";
$bindList['scholarship'] = $scholarship;
}
$stmt = $connection->prepare($searching);
$stmt->bind($bindList);
$stmt->execute();
// Read all rows.
$rows = $stmt->fetchAll('assoc');
// Read rows through iteration.
foreach ($rows as $row) {
// Do work
}
PDO has a limitation and because of that there's no proper way to use the SQL IN() clause in a prepared statement (to bind the values to it), so we need to parse manually the values to be inside that clause as I did in the code.
From the PDO Prepare manual page:
Note: Parameter markers can represent a complete data literal only.
Neither part of literal, nor keyword, nor identifier, nor whatever
arbitrary query part can be bound using parameters. For example, you
cannot bind multiple values to a single parameter in the IN() clause
of an SQL statement.
References
CakePHP 3.3 Cookbook - Database basics
PHP Manual - PHP Data Objects
PHP The Right Way

Benefiting from PDO Prepared statement when used in a function or method

Per http://php.net/manual/en/pdo.prepared-statements.php
The query only needs to be parsed (or prepared) once, but can be
executed multiple times with the same or different parameters. When
the query is prepared, the database will analyze, compile and optimize
its plan for executing the query. For complex queries this process can
take up enough time that it will noticeably slow down an application
if there is a need to repeat the same query many times with different
parameters. By using a prepared statement the application avoids
repeating the analyze/compile/optimize cycle. This means that prepared
statements use fewer resources and thus run faster.
As such, the following will benefit with improved performance when using a prepared statements:
<?php
...
$stmt=$conn->prepare('SELECT a FROM mytable WHERE x=?'); //I often use globals or similar for $conn
foreach($array as $id){
$stmt->execute(array($id));
$data=$stmt->fetchAll(PDO::FETCH_COLUMN);
...
}
...
?>
To eliminate duplicated code, I wish to execute the query in a function.
How could I benefit from the improved performance of a prepared statement under this scenario?
Note the the following code provides no efficiency benefits, and is actually slower than not using prepared statements in the first place.
<?php
function getStuff($x)
{
...
$stmt=$conn->prepare('SELECT a FROM mytable WHERE x=?');
$stmt->execute(array($x));
$data=$stmt->fetchAll(PDO::FETCH_COLUMN);
...
return $data;
};
...
foreach($array as $x){
$data=getStuff($x);
...
}
...
?>
I have used this in the past:
function getStuff($x)
{
global $conn;
static $stmt;
if (null === $stmt) {
$stmt = $conn->prepare('SELECT a FROM mytable WHERE x=?');
}
This works if the function is stateless.
For instance, it would not make sense to make the $conn variable an argument:
function getStuff($conn, $x)
{
static $stmt;
if (null === $stmt) {
$stmt = $conn->prepare('SELECT a FROM mytable WHERE x=?');
}
Because, the static statement won't necessarily belong to the supplied connection.
However you do it, you need to make the statement persist either within the function, or otherwise use the function as a factory and cache the statement elsewhere.
EDIT, testing non-oop scenario:
echo '<pre>';
function do_something()
{
static $i = 0;
echo $i . PHP_EOL;
$i++;
}
do_something();
do_something();
do_something();
do_something();
do_something();
Output:
0
1
2
3
4

convert mysql to pdo

So i have a function thats supposed to handle all data execute operations: sql
function loadResult($sql)
{
$this->connect();
$sth = mysql_query($sql);
$rows = array();
while($r = mysql_fetch_object($sth)) {$rows[] = $r;}
$this->disconnect();
return $rows;
}
I want to convert it to pdo and this is what i have so far: pdo
function loadResult($sql)
{
$this->connect();
$sth = $this->con->prepare($sql);
//execute bind values here
$sth->execute();
$rows = array();
while ( $r = $sth->fetch(PDO::FETCH_OBJ) ) {$rows[] = $r;}
$this->disconnect();
return $rows;
}
Here is an example of a function on how am using it to view data from the database:
function viewtodolist()
{
$db=$this->getDbo(); //connect to database
$sql="SELECT * FROM mcms_todolist_tasks";
//maybe the bind values are pushed into an array and sent to the function below together with the sql statement
$rows=$db->loadResult($sql);
foreach($rows as $row){echo $row->title; //echo some data here }
}
I have just pulled out the important snippets so some variables and methods are from other php classes. Somehow, the mysql query works fine, but the PDO query is giving me headaches on how to include bindValue paremeters most probably in the viewtodolist() function to make it reusable. Any suggestions/recommendations are welcome.
Since your existing function accepts a fully-formed SQL string, with no placeholders, you don't need to use prepare + bind. Your code as written should work fine, or you could use PDO::query() to execute the SQL in one step.
If you want to use parameterised queries, then your loadResult function is going to have to change a bit, as is the way you write your SQL. The example SQL you give doesn't actually have anything in that could be turned into a parameter (column names and table names can't be parameters as discussed here), but I'll use an imaginary variation:
// Get the todo tasks for a particular user; the actual user ID is a parameter of the SQL
$sql = "SELECT * FROM mcms_todolist_tasks WHERE user_id = :current_user_id";
// Execute that SQL, with the :current_user_id parameter pulled from user input
$rows = $db->loadResult($sql, array(':current_user_id' => $_GET['user']));
This is a nice secure way of putting the user input into the query, as MySQL knows which parts are parameters and which are part of the SQL itself, and the SQL part has no variables that anyone can interfere with.
The simplest way of making this work with your existing loadResult function would be something like this:
// Function now takes an optional second argument
// if not passed, it will default to an empty array, so existing code won't cause errors
function loadResult($sql, $params=array())
{
$this->connect();
$sth = $this->con->prepare($sql);
// pass the parameters straight to the execute call
$sth->execute($params);
// rest of function remains the same...
There are cleverer things you can do with parameterised queries - e.g. binding variables to output parameters, preparing a query once and executing it multiple times with different parameters - but those will require more changes to the way your calling code works.

php mysqli should I be using real_escape_string? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Do I need to escape data to protect against SQL injection when using bind_param() on MySQLi?
I have a class that simplifies doing a queries. Here is a code example how to insert data:
$dbi->prepare("INSERT INTO `temp` (`name`,`content`) VALUES (?,?);")->execute($name,$content);
There is a function in class like this:
public function execute(){
if(is_object($this->connection) && is_object($this->stmt)){
if(count($args = func_get_args()) > 0){
$types = array();
$params = array();
foreach($args as $arg){
$types[] = is_int($arg) ? 'i' : (is_float($arg) ? 'd' : 's');
$params[] = $arg;
/*
or maybe $params[] = $this->connection->real_escape_string($arg);
*/
}
array_unshift($params, implode($types));
call_user_func_array(
array($this->stmt, 'bind_param'),
$this->_pass_by_reference($params)
);
}
if($this->stmt->execute()){
$this->affected_rows = $this->stmt->affected_rows;
return $this;
}
else {
throw new Exception;
}
}
else {
throw new Exception;
}
}
When I declare $params[] I have like this $params[] = $arg; Should I put $params[] = $this->connection->real_escape_string($arg); or not?
Thanks.
No. When you use parameters, you don't need to escape the strings.
In fact, you must not escape the strings, because you'll end up storing strings in your database containing literal backslashes. This is not what you want.
According to PHP Manual on mysqli and prepared statements:
Escaping and SQL injection
Bound variables will be escaped automatically by the server. The
server inserts their escaped values at the appropriate places into the
statement template before execution. A hint must be provided to the
server for the type of bound variable, to create an appropriate
conversion. See the mysqli_stmt_bind_param() function for more
information.
The automatic escaping of values within the server is sometimes
considered a security feature to prevent SQL injection. The same
degree of security can be achieved with non-prepared statements, if
input values are escaped correctly.
So no, you don't have to take care about escaping, server will take care about escaping for you.
Escaping manually will result in double escaped values. And in my personal opinion the greatest advantage of placeholders ? in sql statements is that you don't have to take care about escaping.

Dynamic Prepared Statements - Does this open security holes?

This is similar to this question - Are Dynamic Prepared Statements Bad? (with php + mysqli), however since it is 4 years old I wanted to get a more upto date answer.
I've written a class which, although I haven't tested it on more copmlex sql queries, it has worked without fail on simple sql queries, however I'm not sure if doing so has bypassed one of the main reasons for prepared statements - security.
I have made use of the call_user_func_array which was easy enough with the bind_param statements however with the bind_result was a little trickier. I originally used get_result however the host I've gone with doesn't have mysqlnd available, but I managed to get around using the metadata. This is the full class I have written.
Do you think this is secure?
The passed in values are:
$sql is the passed in sql statement:
SELECT * FROM users WHERE id = ? AND created_timestamp > ?
$mysqli is the mysqli connection
$para is the placeholder in the prepared statement:
array ($types = 'ii', 23, 1235376000)
The class:
class crudModel {
function ps($sql, $mysqli, $para) {
//this function should work for just about any simple mysql statement
//for more complicated stuff like joins, unions etc,. we will see
if ($prep = $mysqli->prepare($sql)) {
call_user_func_array(array($prep, 'bind_param'), $this->makeValuesRef($para, $mysqli));
$prep->execute();
$meta = $prep->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($prep, 'bind_result'), $parameters);
while ($prep->fetch()) {
foreach ($row as $key=>$val) {
$x[$key] = $val;
}
$data[] = $x;
}
return $data;
}
}
function makeValuesRef($array, $mysqli) {
$refs = array();
foreach($array as $key => $value) {
$array[$key] = $mysqli->real_escape_string($value); //i don't think escaping is necessary, but it can't hurt (??)
$refs[$key] = &$array[$key];
}
return $refs;
}
}
What you're doing here isn't a dynamic prepared statement. You're just putting some syntatic sugar on top of the MySQLi API (which sucks).
In short, there aren't really any security concerns present from the code you've shown here. In fact, this sort of practice is quite good, because it makes it easier to verify that you're doing it correctly later (since the MySQLi API sucks).
So you're fine. I would worry about the areas you're generating the queries, and ensuring that you're not accidentally putting user-data into them without white-listing...

Categories