replace mysql_real_escape_string for security purposes - mysql

I was using a WordPress Plugin That broke on update to 4.3. The error was
mysql_real_escape_string(): Access denied for user
I found out that the error was because the mysql_real_escape_string is called from within MySQL and needs to be logged in before execution. Solutions were to include a mysql_connect before the mysql_real_escape_string, which solved the problem. But it seems from many of the comments, the mysql_real_escape_string an this solution should not be used for various security reasons. But I am not sure how to change the code to PDO::quote etc as I am not really sure whats going on. The query I am trying to change is
function custom_permalinks_request($query) {
global $wpdb;
global $_CPRegisteredURL;
// First, search for a matching custom permalink, and if found, generate the corresponding
// original URL
$originalUrl = NULL;
// Get request URI, strip parameters and s
$url = parse_url(get_bloginfo('url'));
$url = isset($url['path']) ? $url['path'] : '';
$request = ltrim(substr($_SERVER['REQUEST_URI'], strlen($url)),'/');
$request = (($pos=strpos($request, '?')) ? substr($request, 0, $pos) : $request);
$request_noslash = preg_replace('#/+#','/', trim($request, '/'));
if ( !$request ) return $query;
$sql = "SELECT $wpdb->posts.ID, $wpdb->postmeta.meta_value, $wpdb->posts.post_type FROM $wpdb->posts ".
"LEFT JOIN $wpdb->postmeta ON ($wpdb->posts.ID = $wpdb->postmeta.post_id) WHERE ".
" meta_key = 'custom_permalink' AND ".
" meta_value != '' AND ".
" ( LOWER(meta_value) = LEFT(LOWER('".mysql_real_escape_string($request_noslash)."'), LENGTH(meta_value)) OR ".
" LOWER(meta_value) = LEFT(LOWER('".mysql_real_escape_string($request_noslash."/")."'), LENGTH(meta_value)) ) ".
"ORDER BY LENGTH(meta_value) DESC LIMIT 1";
$posts = $wpdb->get_results($sql);
return $query;
}
Is there an easy way to replace the mysql_real_escape_string or better way to do this? I am not sure this question is similar, but I dont see how to implement the answers.

Related

Prevent MySQL Injection on search and dropdowns

I know this might have been asked before but I am trying to protect my search field and drop downs from MySQL injection and am having trouble integrating mysql_real_escape_string into my PHP. I am currently filtering my search results by keywords in 2 drop downs or by a freeform input where the user types in a reference. I've commented below where I am trying to add the escape string but it is breaking my search function. Can anyone advise me on what to do? Thanks for any help
<?php
// SEARCH FROM TEXT INPUT
mysql_select_db($database_connectInfo, $connectInfo);
if (isset($_POST['searchByRef']))
{
$searchword = $_POST['searchByRef'];
//ESCAPE STRING HERE
$searchword = mysql_real_escape_string($connectInfo, $searchword);
$query_dbname = "SELECT * FROM dbname WHERE `ref` LIKE '%".$searchword."%'";
}
else
// SEARCH FROM DROPDOWN MENUS
if (isset($_REQUEST['submit']))
{
$drop1 = $_POST['search1'];
$drop2 = $_POST['search2'];
//ESCAPE STRING HERE
$drop1 = mysql_real_escape_string($connectInfo, $drop1);
$drop2 = mysql_real_escape_string($connectInfo, $drop2);
$query_dbname = 'SELECT * FROM dbname WHERE 1=1' . ($drop1 ? ' AND `colour` LIKE "%' . $drop1 . '%"' : '') . ($drop2 ? ' AND `style` LIKE "%' . $drop2 . '%"' : ' ORDER BY id DESC');
}
else
{
$query_dbname = "SELECT * FROM dbname ORDER BY ref DESC";
}
$dbname = mysql_query($query_dbname, $connectInfo) or die(mysql_error());
$row_dbname = mysql_fetch_assoc($dbname);
$totalRows_all = mysql_num_rows($dbname);
?>
Don't use mysql_escape_string.. instead use mysqli or PDO with prepared statements.
http://www.php.net/manual/en/book.pdo.php
For more info on WHY see this:
Why mysql_real_escape_string() did not prevent hack?

Mysql dynamic sql statement with PDO and parameters

I want to create a dynamic sql statement and run it using PDO. My problem is that i have some parameters and i cannot think of a way to pass the parameters.
Ex :
$query = "Select * from tbl_task where 1=1";
if (!empty($name)) $query .= " AND name = ?";
if (!empty($status)) $query .= " AND status = ?"
$db_stmt = new PDOStatement();
$db_stmt = $this->db->prepare($query);
$db_stmt->bindParam (1,$name);
$db_stmt->bindParam (2,$status);
My parameters does not get binded and i don't know how many parameters i have to bind, unless i write the same if statements but with bindParam instructions.
I tryed with mysql_real_escape_string instead bindParam to PDO but for some reason my parameters are added empty.
Any idea on how can i build a dynamic query and bind parameters to PDO ?
Edit 1 :
$arr = array();
if (!empty($name)){
$query .= " AND `name` like :NAME";
$arr['NAME'] = $name;
}
$db_stmt = new PDOStatement();
$db_stmt = $this->db->prepare($query);
$db_stmt->execute($arr);
How can i write a "like" statement ? I tried
$query .= " AND `name` like :NAME" . "%";
and is not working.
What I usually do is the following:
$query = "Select * from `tbl_task` where 1=1";
$arr = array();
if (!empty($name))
{
$query .= " AND `name` = :NAME";
$arr['NAME'] = $name;
}
if (!empty($status))
{
$query .= " AND `status` = :STATUS";
$arr['STATUS'] = $status;
}
$this->db->beginTransaction();
try
{
$tmp = $this->db->prepare($query);
$tmp->execute($arr);
$this->db->commit();
}
catch(PDOException $ex)
{
$this->db->rollBack();
$this->log->error($ex->getMessage());
}
You can't add SQL code as a parameter; only data will do. You'll have to force these bits into $query. They won't be escaped then so they shouldn't contain user-submitted data.
What I usually do is the following:
$query = "Select * from tbl_task where 1=1";
if (!empty($name)) $query .= $db->parse(" AND name = ?s", $name);
if (!empty($status)) $query .= $db->parse(" AND status = ?s",$status);
$data = $this->db->getAll($query);
the idea is in having a function to parse placeholders in arbitrary query part instead of whole query.
I don't bother with native prepared statements though. They pollute PHP scripts with heaps of useless code with not a single benefit.
To answer updated question
as you've been told, you can't bind arbitrary query part. But a literal only.
So, make your literal looks like foo% and then bind it usual way.

Why doesn't this custom table $wpdb query work?

I have a custom table called wp_purchases that is added to by a Paypal IPN, it has the columns post_id, purchase_id, username, email and changes:
$user = $_POST['username'];
$post_id = $post->ID;
global $wpdb;
$wpdb->purchases = $table_prefix . 'purchases';
$result = $wpdb->query("SELECT * FROM $wpdb->purchases WHERE username = $user AND post_id = $post_id");
if ($result) {...
I'm just trying to check if there is a purchase under a certain username in a single .php theme page and then perform an operation unrelated to the $result.
It won't work though, any way I try it comes up with null. Is it something to do with using $wpdb->purchases or not including a certain file or something else? I've also tried it with get_results and get_row with no luck.
Try this :
$result = $wpdb->query("SELECT * FROM `".$wpdb->purchases."` WHERE username = '$user' AND post_id = '$post_id';");
I gave up and just used
$result = $wpdb->query("SELECT * FROM wp_purchases WHERE username = $user AND post_id = $post_id");
because it's for a single site and doesn't really need the table prefix, it'll always be wp_
I know it's too late for this answer, but I'll still post it for the sake of others who are also having similar issue.
You mentioned that the query still returns nothing even after removing the WHERE part, have you tried to echo $wpdb->purchases variable? It might just returning you a string "purchases" instead of "wp_purchases" (assuming that your table prefix is "wp_"). Try using $wpdb->prefix instead of $table_prefix, or $wpdb->base_prefix if you got a multisite.
$wpdb->purchases = $wpdb->prefix . 'purchases';
What type of username? I assume it stores string data. So variable $user must be quoted:
"SELECT * FROM $wpdb->purchases WHERE username = \"$user\" AND post_id = $post_id"

Using mySQL/Wordpress to get data from a metadata column

I am using wordpress metadata to store dynamic data.
I've ran into a wall and need some help,I have once piece of meta data that basically is a multidimensional array.
The issue I have is how the metadata is stored I'm having a hard time retrieving what i need.
At the bottom I have included how wordpress stores the data (it's one column)
This is how the first entry looks like before it's stored
shopper_wp_id:3
shopper_status_time:2011-11-29 17:24:49
shopper_status_comment:COMMENTS
shopper_status:declined
shopper_answers:["TEST1","TEST2","TEST3","TEST4","TEST5","TEST6"]
shopper_preapproval_status:maybe
What i need to be able to do is find all the entries where the shopper_wp_id = 3 and the shopper_status = declined
If those 2 values where next to each other but they are separated by a datetime that is ever changing. Any ideas?
a:4:{i:0;a:6:{s:13:"shopper_wp_id";s:1:"3";s:19:"shopper_status_time";s:19:"2011-11-29 17:24:49";s:22:"shopper_status_comment";s:8:"COMMENTS";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:5:"TEST1";i:1;s:5:"TEST2";i:2;s:5:"TEST3";i:3;s:5:"TEST4";i:4;s:5:"TEST5";i:5;s:5:"TEST6";}s:26:"shopper_preapproval_status";s:5:"maybe";}i:1;a:7:{s:13:"shopper_wp_id";s:4:"2063";s:19:"shopper_status_time";s:19:"2011-11-30 16:37:52";s:22:"shopper_status_comment";s:17:"sgdfsgfgdfdfgfhsh";s:14:"shopper_status";s:4:"paid";s:15:"shopper_answers";a:6:{i:0;s:10:"sdfsadfdfs";i:1;s:11:"dfgdsfgsdfg";i:2;s:10:"sdfgfdsfdg";i:3;s:9:"dgsdfgdfg";i:4;s:10:"sgdfsgfdgd";i:5;s:10:"sdfgfgfgds";}s:26:"shopper_preapproval_status";s:3:"yes";s:13:"blog_post_url";s:17:"http://google.com";}i:2;a:6:{s:13:"shopper_wp_id";s:4:"2916";s:19:"shopper_status_time";s:19:"2011-11-29 20:13:13";s:22:"shopper_status_comment";s:7:"dfbdfdf";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:15:"cvczxvzxcvzxcbz";i:1;s:11:"zcvfxzbxbxb";i:2;s:8:"zvzxcbzb";i:3;s:10:"zfdbfdbdfb";i:4;s:11:"zdfbdfbfbdf";i:5;s:6:"bfdfdh";}s:26:"shopper_preapproval_status";s:2:"no";}i:3;a:6:{s:13:"shopper_wp_id";s:4:"1614";s:19:"shopper_status_time";s:19:"2011-11-29 20:16:06";s:22:"shopper_status_comment";s:15:"sfdhfsdhsdfhdsh";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:8:"sdfsdfsd";i:1;s:15:"zvzfbdfbsdfbdbd";i:2;s:15:"dfgdsfhsfdsfhdf";i:3;s:17:"xfbfghfghnfsgnfgn";i:4;s:17:"dsfgshfdshsfdghsg";i:5;s:12:"sdffsdhsdfhh";}s:26:"shopper_preapproval_status";s:2:"no";}}
The string is serialized, so you need to unserialize it, then you can more easily get the information you need using the resulting structure (in this case, an associative array). For example
$data = 'a:4:{i:0;a:6:{s:13:"shopper_wp_id";s:1:"3";s:19:"shopper_status_time";s:19:"2011-11-29 17:24:49";s:22:"shopper_status_comment";s:8:"COMMENTS";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:5:"TEST1";i:1;s:5:"TEST2";i:2;s:5:"TEST3";i:3;s:5:"TEST4";i:4;s:5:"TEST5";i:5;s:5:"TEST6";}s:26:"shopper_preapproval_status";s:5:"maybe";}i:1;a:7:{s:13:"shopper_wp_id";s:4:"2063";s:19:"shopper_status_time";s:19:"2011-11-30 16:37:52";s:22:"shopper_status_comment";s:17:"sgdfsgfgdfdfgfhsh";s:14:"shopper_status";s:4:"paid";s:15:"shopper_answers";a:6:{i:0;s:10:"sdfsadfdfs";i:1;s:11:"dfgdsfgsdfg";i:2;s:10:"sdfgfdsfdg";i:3;s:9:"dgsdfgdfg";i:4;s:10:"sgdfsgfdgd";i:5;s:10:"sdfgfgfgds";}s:26:"shopper_preapproval_status";s:3:"yes";s:13:"blog_post_url";s:17:"http://google.com";}i:2;a:6:{s:13:"shopper_wp_id";s:4:"2916";s:19:"shopper_status_time";s:19:"2011-11-29 20:13:13";s:22:"shopper_status_comment";s:7:"dfbdfdf";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:15:"cvczxvzxcvzxcbz";i:1;s:11:"zcvfxzbxbxb";i:2;s:8:"zvzxcbzb";i:3;s:10:"zfdbfdbdfb";i:4;s:11:"zdfbdfbfbdf";i:5;s:6:"bfdfdh";}s:26:"shopper_preapproval_status";s:2:"no";}i:3;a:6:{s:13:"shopper_wp_id";s:4:"1614";s:19:"shopper_status_time";s:19:"2011-11-29 20:16:06";s:22:"shopper_status_comment";s:15:"sfdhfsdhsdfhdsh";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:8:"sdfsdfsd";i:1;s:15:"zvzfbdfbsdfbdbd";i:2;s:15:"dfgdsfhsfdsfhdf";i:3;s:17:"xfbfghfghnfsgnfgn";i:4;s:17:"dsfgshfdshsfdghsg";i:5;s:12:"sdffsdhsdfhh";}s:26:"shopper_preapproval_status";s:2:"no";}}';
$data = unserialize($data);
foreach ($data as $shopper){
if(isset($shopper['shopper_wp_id']) && $shopper['shopper_wp_id'] == 3){
//do something
}
}
Thanks everyone but i figured it out, not sure it's the best but it seems to be pretty quick..
I ended up using a combination of mySQL's LOCATE and SUBSTRING with a HAVING kicker.
Below is my function and then below that is the SQL that it creates.
function db_get_users_shops_by_shopper_status($shopper_wp_id, $limit = 0, $shopper_status) {
global $wpdb;
if (!is_array($shopper_status)) {
$shopper_status = array($shopper_status);
}
$sql = "
SELECT wp_posts . *,
SUBSTRING(meta_value,
LOCATE('{s:13:\"shopper_wp_id\";s:" . strlen($shopper_wp_id) . ":\"" . $shopper_wp_id . "\";', meta_value),
LOCATE('s:15:\"shopper_answers\";', meta_value))
AS shopper_entity
FROM wp_posts, wp_postmeta
WHERE post_type='shoppertunity'
AND wp_posts.ID = wp_postmeta.post_id AND meta_key = 'shoppers'
AND meta_value LIKE '%s:13:\"shopper_wp_id\";s:" . strlen($shopper_wp_id) . ":\"" . $shopper_wp_id . "\";%'
HAVING shopper_entity LIKE ";
for ($i = 0; $i < sizeof($shopper_status); ++$i) {
if ($i > 0) {
$sql .= " OR shopper_entity LIKE ";
}
$sql .= " '%s:14:\"shopper_status\";s:" . strlen($shopper_status[$i]) . ":\"" . $shopper_status[$i] . "\";%'";
}
if ($limit != 0) {
$sql .= "LIMIT " . $limit;
}
//echo($sql ."<br/><br/>");
$results = $wpdb -> get_results($sql);
return $results;
}
SQL Results
SELECT
`wp_posts`.*,
SUBSTRING(`meta_value`,
LOCATE('{s:13:"shopper_wp_id";s:4:"2063";',
`meta_value`),
LOCATE('s:15:"shopper_answers";', `meta_value`)) AS `shopper_entity`
FROM
wp_posts,
wp_postmeta
WHERE
post_type = 'shoppertunity' AND wp_posts.ID = wp_postmeta.post_id AND meta_key = 'shoppers' AND meta_value LIKE '%s:13:"shopper_wp_id";s:1:"3";%'
HAVING shopper_entity LIKE '%s:14:"shopper_status";s:9:"completed";%' OR shopper_entity LIKE '%s:14:"shopper_status";s:4:"paid";%'

"No result was found for query although at least one row was expected." Query should display records though in Symfony

I'm trying to retrieve content using two items in the URL. Here is the php/symfony code that should do it:
$em = $this->getDoctrine()->getEntityManager();
$repository = $this->getDoctrine()
->getRepository('ShoutMainBundle:Content');
$query = $repository->createQueryBuilder('p')
->where('p.slug > :slug')
->andWhere('p.subtocontentid > :parent')
->setParameters(array(
'slug' => $slug,
'parent' => $page
))
->getQuery();
$content = $query->getSingleResult();
However, when this code is executed it returns the following error:
No result was found for query although at least one row was expected.
I have done some tests, and the data held in the $slug and $page variables hold the correct information. I have also tested the MySQL query and the query brings up the desired result, which confuses me further.
Have I missed something?
As it was answered here
You are getting this error because you are using the
getSingleResult() method. it generates an Exception if it can't find
even a single result. you can use the getOneOrNullResult() instead
to get a NULL if there isn't any result from the query.
Query#getSingleResult(): Retrieves a single object. If the result
contains more than one object, an NonUniqueResultException is thrown.
If the result contains no objects, an NoResultException is thrown. The
pure/mixed distinction does not apply.
No result was found for query although at least one row was expected.
Another reason could be:
You did this
$query = $this->getEntityManager()
->createQuery('
SELECT u FROM MyBundle:User u
WHERE u.email = :email')
->setParameter('email', $email);
return $query->getSingleResult();
Instead of this
$query = $this->getEntityManager()
->createQuery('
SELECT u FROM MyBundle:User u
WHERE u.email = :email')
->setParameter('email', $email);
$query->setMaxResults(1);
return $query->getResult();
Don't you want to use "=" instead of ">" ?
If you've got this message because used
$content = $query->getSingleResult();
you can just replace it with the row below
$content = $query->getOneOrNullResult(AbstractQuery::HYDRATE_SINGLE_SCALAR) ?? 0;