Securing PHP MySQL code - php

I have a PHP search suggestion script which uses MySQL as its back-end. I am aware there are many vunerabilities in my code, I was just wondering what I can do to make it more secure.
Here is my code:
<?php
$database=new mysqli('localhost','username','password','database');
if(isset($_POST['query'])){
$query=$database->real_escape_string($_POST['query']);
if(strlen($query)>0){
$suggestions=$database->query(
"SELECT * FROM search WHERE name LIKE '%" . $query .
"%' ORDER BY value DESC LIMIT 5");
if($suggestions){
while($result=$suggestions->fetch_object()){
echo '<a>'.$result->name.'</a>';
}
}
}
}
?>

Actually there aren't, considering you are escaping the only external value in the SQL
Anyway I suggest you to use PDO::prepare for queries. Go here for further infos
http://it.php.net/manual/en/pdo.prepare.php
Example:
$sth = $dbh->prepare('SELECT * FROM article WHERE id = ?');
$sth->execute(array(1));
$red = $sth->fetchAll();

Some tips from me:
use PDO,
don't concatenate query parameters, use prepared statements in PDO,
don't put "*" in SELECT statement, get only the columns you'll need,
use fetchAll() in PDO, don't fetch records in while() loop.

Related

Do not execute the command or for SQL PHP code

Why does the following code not work when I use the command or?
<?php
include "connect.php";
$sort=$_POST["sort"];
$query="SELECT * FROM product WHERE $sort='0' ORDER BY id DESC OR $sort='1' ORDER BY id ASC ";
$stmt = $conn->prepare($query);
$stmt->execute();
$product=array();
while ($row=$stmt->fetch(PDO::FETCH_ASSOC)){
$record["id"]=$row["id"];
$record["title"]=$row["title"];
$product[]=$record;
}
echo JSON_encode($product);
What is it you're trying to attempt here? It looks like you're trying to dump an if else statement within the sql query. That's not the correct way to do it. If I'm correct with what Im saying. You can try something like this
if (Something == SomethingElse) {
$query="SELECT * FROM product WHERE $sort='0' ORDER BY id DESC"
} else {
$query="SELECT * FROM product WHERE $sort='1' $sort='1' ORDER BY id ASC"
}
Something along those routes. Also, I highly suggest you use a foreach statement for your rows instead of assigning them individually.
$query="SELECT * FROM product WHERE $sort='0' ORDER BY id DESC"
foreach ($query as $row) {
$row["id"];
$row["title"];
}
Remember this is just an example, so you'll have to work out the rest of the code yourself. Since you shared so little of what these variables exactly mean its hard for me to understand what you precisely want.
More about Foreach
You can't do that, you can't change the order of SELECT using OR. The OR is only for WHERE statements.
You can try to create one query and next using IF to make one or another query with ORDER BY.
Another thing, be carefully with inject strings from POST directly to your SQL statement, it is very insecure. More on SQL injection.

Can I use name of the column as parameter in PDO?

I'm trying to execute this:
$colparam = 'abcd';
$stmt = $db->prepare("SELECT DISTINCT ? AS kol FROM katalog ORDER BY kol ASC");
$stmt->execute(array($colparam));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
and it's not working (no errors, just empty array as result).
Instead this works fine:
$stmt = $db->prepare("SELECT DISTINCT abcd AS kol FROM katalog ORDER BY kol ASC");
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
So is there any catch with the use of parameter as a name of the column in PDO?
No, you can't use parameter replacements for any database objects (tables, columns, etc.) in MySQL.
When you think about what a prepared statement actually is, this makes complete sense. As how can MySQL prepare a query execution plan when it does not even know the database objects involved.
I certainly wish that more documentation would actually cover what a prepared statement actually does (beyond it's obvious use for parametrization).
Here is link to MySQL prepared statement documentation for more reading:
https://dev.mysql.com/doc/refman/5.6/en/sql-syntax-prepared-statements.html

Efficiently getting number of rows returned of SELECT query with WHERE clause using PDO

There are numerous discussions on SO regarding how to get the number of rows returned when running a SELECT query using PDO. While most (including the PHP manual) suggest using two queries, with the first running COUNT(), I haven't seen one that suggested how to easily do this using prepared statements with WHERE clauses.
How do I most-efficiently (both in processing and number of lines of code) run a COUNT() using the same WHERE clause? The prepared query already has the columns specified. fetchAll() won't work here because that won't scale; if I have to return millions of rows, processing it using fetchAll would be super slow.
For example, without the count:
$sql = "SELECT
FirstName,
LastName
FROM
People
WHERE
LastName = :lastName";
$query = $pdoLink->prepare($sql);
$query->bindValue(":lastName", '%Smith%');
$query->execute();
while($row = $query->fetch(PDO::FETCH_ASSOC)) {
echo $row['FirstName'] . " " . $row['LastName'];
}
I looked at just adding COUNT(ID) to the SELECT clause, and having it be just one query, but it looks like there is no real good way (or not database-specific way) of rewinding the fetch() once I get a row from it.
Another solution could be making the WHERE clause it's own variable that is built. But, that doesn't seem very efficient. It's preparing two queries, binding the values all over again, and executing it.
So something like:
$whereClause = " WHERE
LastName = :lastName";
$rowsSql = "SELECT
COUNT(ID) As NumOfRows
FROM
People " . $whereClause;
$rowsQuery = $pdoLink->prepare($sql);
$rowsQuery->bindValue(":lastName", '%Smith%');
$rowsQuery->execute();
if ($rowsQuery->fetchColumn() >= 1)
//Prepare the original query, bind it, and execute it.
$sql = "SELECT
FirstName,
LastName
FROM
People " . $whereClause;
$query = $pdoLink->prepare($sql);
$query->bindValue(":lastName", '%Smith%');
$query->execute();
while($row = $query->fetch(PDO::FETCH_ASSOC)) {
echo $row['FirstName'] . " " . $row['LastName'];
}
}
else
{
//No rows found, display message
echo "No people found with that name.";
}
When using MySQL, PDOStatement::rowCount() returns the number of rows in the result set. It actually calls the underlying mysql_num_rows() C function to populate the value. No need for multiple queries or any other messing around.
This is true of MySQL, but this behaviour cannot be relied on for other drivers (others may support it but it's not guaranteed, I'm not familiar with others enough to say for sure either way). But since your question regards specifically MySQL, it should serve your purposes.
Try this built-in PDO function;
$query->rowCount();

how to securely use LIKE

I'm building a script where users can search a database. my understanding is that PDO doesn't let you set a parameter for the LIKE operand. So I have this code to make up for it
$sQuery = "SELECT * FROM table WHERE column LIKE '%" . $this->sQuery . "%' LIMIT 30";
$Statement = $this->Database->prepare($sQuery);
$Statement->execute();
I doubt this is secure against SQL injection. Is there any way to make it secure?
You're right, interpolating any value into an SQL string creates a risk for SQL injection vulnerability. It's better to use a SQL query parameter placeholder when you prepare(), and then supply the value as a parameter when you execute().
$pattern = "%" . $this->sQuery . "%";
$sQuery = "SELECT * FROM table WHERE column LIKE ? LIMIT 30";
$Statement = $this->Database->prepare($sQuery);
$Statement->execute(array($pattern));
Take that as pseudocode because I can't tell from your example which MySQL extension you're using. I'm assuming PDO, which allows parameters to be sent as an array argument to execute().
Some people use PDOStatement::bindParam(), but there's no advantage to doing so. Maybe in some other RDBMS brands the PDO::PARAM_STR matters, but in the MySQL driver, the parameter type is ignored.
PS: Aside from the security issue you asked about, you will find a search for wildcard-based patterns like you're doing don't perform well as your data grows larger. See my presentation Full Text Search Throwdown.
This should work:
$sQuery = "SELECT * FROM table WHERE column LIKE :query LIMIT 30";
$Statement = $this->Database->prepare($sQuery);
$Statement->execute(array(':query' => '%' . $this->sQuery . '%'));

mysql like statement is not working as expected

I have a table with 4 record.
Records: 1) arup Sarma
2) Mitali Sarma
3) Nisha
4) haren Sarma
And I used the below SQL statement to get records from a search box.
$sql = "SELECT id,name FROM ".user_table." WHERE name LIKE '%$q' LIMIT 5";
But this retrieve all records from the table. Even if I type a non-existence word (eg.: hgasd or anything), it shows all the 4 record above. Where is the problem ? plz any advice..
This is my full code:
$q = ucwords(addslashes($_POST['q']));
$sql = "SELECT id,name FROM ".user_table." WHERE name LIKE '%".$q."' LIMIT 5";
$rsd = mysql_query($sql);
Your query is fine. Your problem is that $q does not have any value or you are appending the value incorrectly to your query, so you are effectively doing:
"SELECT id,name FROM ".user_table." WHERE name LIKE '%' LIMIT 5";
Use the following code to
A - Prevent SQL-injection
B - Prevent like with an empty $q
//$q = ucwords(addslashes($_POST['q']));
//Addslashes does not work to prevent SQL-injection!
$q = mysql_real_escape_string($_POST['q']);
if (isset($q)) {
$sql = "SELECT id,name FROM user_table WHERE name LIKE '%$q'
ORDER BY id DESC
LIMIT 5 OFFSET 0";
$result = mysql_query($sql);
while ($row = mysql_fetch_row($result)) {
echo "id: ".htmlentities($row['id']);
echo "name: ".htmlentities($row['name']);
}
} else { //$q is empty, handle the error }
A few comments on the code.
If you are not using PDO, but mysql instead, only mysql_real_escape_string will protect you from SQL-injection, nothing else will.
Always surround any $vars you inject into the code with single ' quotes. If you don't the escaping will not work and syntax error will hit you.
You can test an var with isset to see if it's filled.
Why are you concatenating the tablename? Just put the name of the table in the string as usual.
If you only select a few rows, you really need an order by clause so the outcome will not be random, here I've order the newest id, assuming id is an auto_increment field, newer id's will represent newer users.
If you echo data from the database, you need to escape that using htmlentities to prevent XSS security holes.
In mysql, like operator use '$' regex to represent end of any string.. and '%' is for beginning.. so any string will fall under this regex, that's why it returms all records.
Please refer to http://dev.mysql.com/doc/refman/5.0/en/pattern-matching.html once. Hope, this will help you.

Categories