Converting to PDO Parameter Binding - php

I'm using SensioLabsInsight to profile any vulnerabilities in my code.
I've received several errors for possible sql injection, and it recommends using parameter binding with PDO. This is fine since I'm already using PDO for my db driver.
Right now my model is passed a $data array and then checks for specific values in the array in order to add to the sql query if present, like so:
public function getDownloads($data = array()) {
$sql = "
SELECT *
FROM {$this->db->prefix}download d
LEFT JOIN {$this->db->prefix}download_description dd
ON (d.download_id = dd.download_id)
WHERE dd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
if (!empty($data['filter_name'])) {
$sql .= " AND dd.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
}
$sort_data = array(
'dd.name',
'd.remaining'
);
if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
$sql .= " ORDER BY " . $data['sort'];
} else {
$sql .= " ORDER BY dd.name";
}
if (isset($data['order']) && ($data['order'] == 'DESC')) {
$sql .= " DESC";
} else {
$sql .= " ASC";
}
if (isset($data['start']) || isset($data['limit'])) {
if ($data['start'] < 0) {
$data['start'] = 0;
}
if ($data['limit'] < 1) {
$data['limit'] = 20;
}
$sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
}
$query = $this->db->query($sql);
return $query->rows;
}
The error referenced from the SensioLabsInsight analysis references only the $data['sort'] clause as being a possible injection point.
My question is, do I need to test for $data array presence when creating a prepare statement, or will it simply return null if the array value is empty.
My proposed new query with parameter binding would look like so:
public function getDownloads($data = array()) {
$sql = "
SELECT *
FROM {$this->db->prefix}download d
LEFT JOIN {$this->db->prefix}download_description dd
ON (d.download_id = dd.download_id)
WHERE dd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
if (!empty($data['filter_name'])) {
$sql .= " AND dd.name LIKE :filter_name%";
}
$sort_data = array(
'dd.name',
'd.remaining'
);
if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
$sql .= " ORDER BY :sort";
} else {
$sql .= " ORDER BY dd.name";
}
if (isset($data['order']) && ($data['order'] == 'DESC')) {
$sql .= " DESC";
} else {
$sql .= " ASC";
}
if (isset($data['start']) || isset($data['limit'])) {
if ($data['start'] < 0) {
$data['start'] = 0;
}
if ($data['limit'] < 1) {
$data['limit'] = 20;
}
$sql .= " LIMIT :start, :limit";
}
$this->db->prepare($sql);
$this->db->bindParam(':filter_name', $data['filter_name']);
$this->db->bindParam(':sort', $data['sort']);
$this->db->bindParam(':start', $data['start'], PDO::PARAM_INT);
$this->db->bindParam(':limit', $data['limit'], PDO::PARAM_INT);
$query = $this->db->execute();
return $query->rows;
}
Will this work as is, or do the parameter bindings need to be moved within the if/else conditionals?

Related

Unable to SELECT form DB with ORDER BY and php IF

i use following to statement to load data form SQL the SELECT query works well until php IF is executed.
i want to use 2 ORDER BY in single statement when if statement is executed i get
Fatal error : Uncaught exception 'PDOException' with message
'SQLSTATE[42000]: Syntax error or access violation: 1064 You have an
error in your SQL syntax; check the manual that corresponds to your
MySQL server version for the right syntax to use near 'AND sca IN (?)'
at line 1' in
C:\Users\Amin\Documents\NetBeansProjects\fetch.php:34 Stack trace:
0 C:\Users\Amin\Documents\NetBeansProjects\fetch.php(34): PDO->prepare('SELECT * FROM a...') #1 {main} thrown in
C:\Users\Amin\Documents\NetBeansProjects\fetch.php on line 34
How do i solve this problem
if (isset($_POST["action"])) {
$query = "SELECT * FROM allpostdata WHERE sts = '1' AND mca='Vehicle' ORDER BY pdt DESC";
if (!empty($_POST['cate'])) {
$query .= " AND sca IN (" . str_repeat("?,", count($_POST['cate']) - 1) . "?)";
} else {
$_POST['cate'] = []; // in case it is not set
}
if (!empty($_POST['brand'])) {
$query .= " AND product_brand IN (" . str_repeat("?,", count($_POST['brand']) - 1) . "?)";
} else {
$_POST['brand'] = []; // in case it is not set
}
if (!empty($_POST['model'])) {
$query .= " AND mdl IN (" . str_repeat("?,", count($_POST['model']) - 1) . "?)";
} else {
$_POST['model'] = []; // in case it is not set
}
if (!empty($_POST['sort'])) {
if ($_POST["sort"][0] == "ASC" || $_POST["sort"][0] == "DESC") { //simplistic whitelist
$query .= " ORDER BY prs " . $_POST['sort'][0];
}
}
$stmt = $conn->prepare($query);
$params = array_merge($_POST['cate'], $_POST['brand'], $_POST['model']);
$stmt->execute($params);
$result = $stmt->fetchAll();
$total_row = $stmt->rowCount();
$output = '';
As already mentioned by #aynber, the order by should be the last clause in your query. Thus, the correct form would be as below:
if (isset($_POST["action"])) {
$query = "SELECT * FROM allpostdata WHERE sts = '1' AND mca='Vehicle'";
if (!empty($_POST['cate'])) {
$query .= " AND sca IN (" . str_repeat("?,", count($_POST['cate']) - 1) . "?)";
} else {
$_POST['cate'] = []; // in case it is not set
}
if (!empty($_POST['brand'])) {
$query .= " AND product_brand IN (" . str_repeat("?,", count($_POST['brand']) - 1) . "?)";
} else {
$_POST['brand'] = []; // in case it is not set
}
if (!empty($_POST['model'])) {
$query .= " AND mdl IN (" . str_repeat("?,", count($_POST['model']) - 1) . "?)";
} else {
$_POST['model'] = []; // in case it is not set
}
$query .= " ORDER BY pdt DESC";
if (!empty($_POST['sort'])) {
if ($_POST["sort"][0] == "ASC" || $_POST["sort"][0] == "DESC") { //simplistic whitelist
$query .= ", prs " . $_POST['sort'][0];
}
}

Filter data from database with multiple user selections

Currently I'm developing a search form so my SQL query needs to change with user input. Please see the below code sample.
$sqlSearch = "SELECT * FROM seafarers WHERE ";
if ($dateS != "") {
$sqlSearch .= "add_date = '" . changeDateSlashToHypen($dateS) . "' and ";
}
if ($cdcS != "") {
$sqlSearch .= "cdc = '" . $cdcS . "' and ";
}
if ($ppS != "") {
$sqlSearch .= "passport LIKE '%$ppS%' and ";
}
if ($surnameS != "") {
$sqlSearch .= "surname LIKE '" . $surnameS . "%' and ";
In order to execute this statement the user must select all the options; the statement will not work if the user selects one or two options.
Don't patch your query together like this. Use Prepared Statements. Example:
SELECT *
FROM seafarers
WHERE (:dt is null or add_date = :dt)
and (:cdc is null or cdc = :cdc)
You have to fill the parameters of the query before execution.
Start out with a placeholder like 1=1 which will always be true, and then use AND as a prefix instead of a suffix.
$sqlSearch = "SELECT * FROM seafarers WHERE 1=1 ";
if ($dateS != "") {
$sqlSearch .= " AND add_date = '" . changeDateSlashToHypen($dateS) . "'";
}
...
But as pointed out in the other answer you need to use prepared statements. So, assuming you're using mysqli, which everyone seems to do for some reason:
$sqlSearch = "SELECT * FROM seafarers WHERE 1=1 ";
$types = "";
$parameters = [];
if ($dateS != "") {
$sqlSearch .= " AND add_date = ?";
$types .= "s";
$parameters[] = changeDateSlashToHypen($dateS);
}
if ($cdcS != "") {
$sqlSearch .= " AND cdc = ?";
$types .= "s";
$parameters[] = $cdcS;
}
if ($ppS != "") {
$sqlSearch .= " AND passport LIKE ?";
$types .= "s";
$parameters[] = "%$ppS%";
}
if ($surnameS != "") {
$sqlSearch .= " AND surname LIKE ?";
$types .= "s";
$parameters[] = "$surnameS%";
}
$stmt = $db->prepare($sqlSearch);
if (count($parameters) {
$stmt->bind_param($types, ...$parameters);
}
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
...
}

How to print the result of a Phalcon query result?

There is a Phalcon query :
function lireParCritere($critere) {
$sSQL = "
SELECT c.salle_code,c.salle_lib,c.salle_comment
FROM salle as c WHERE 1 = 1 ";
if(isset($critere["salle_code"]) && $critere["salle_code"] != "") {
$sSQL .= "AND c.salle_code = ' " . $critere["salle_code"] . "' ";
}
$query = new Query($sSQL,$this->getDI());
$ret = $query->execute();
return $ret;
}
How to print the result of this query ?
\Phalcon\Mvc\Model\Query uses PHQL, not SQL.
public function lireParCritere($critere)
{
$model = '\Namespaced\Path\To\Salle';
$sSQL = "
SELECT c.salle_code,c.salle_lib,c.salle_comment
FROM $model as c WHERE 1 = 1 ";
if(isset($critere["salle_code"]) && $critere["salle_code"] != "") {
$sSQL .= "AND c.salle_code = ' " . $critere["salle_code"] . "' ";
}
$query = new \Phalcon\Mvc\Model\Query($sSQL, $this->getDI());
$ret = $query->execute();
return $ret;
}
public function test()
{
foreach ($this->lireParCritere([]) as $row) {
/** #var \Phalcon\Mvc\Model\Row $row */
assert($row->salle_code === $row->readAttribute('salle_code'));
}
}

two methods in a class so close in what they do

I have this class that has these two methods that are so closely related to the each other. I do not want to pass the flags so I kept them separate. I was wondering if there is a way to rewrite it so that I do not have to repeat so closely!
class Test extends Controller
{
public static function nonFormattedData($param)
{
$arr = array();
if (is_array($param)) {
$i = 0;
$sql = "
select *
from table1
where
";
if (isset($param['startDate'])) {
$sql .= " date_created between ? AND ?";
$arr[] = $param['startDate'];
$arr[] = $param['endDate'];
$i++;
}
if (isset($param['amount']) && !empty($param['amount'])) {
if ($i > 0) $sql .= " AND ";
$sql .= " balance= ?";
$arr[] = $param['amount'];
$i++;
}
if (isset($param) && !empty($param['amount'])) {
if ($i > 0) $sql .= " AND ";
$sql .= " balance= ?";
$arr[] = $param['amount'];
$i++;
}
if (isset($param['createdBy']) && !empty($param['createdBy'])) {
if ($i > 0) $sql .= " AND ";
$sql .= " column2 like '%Created By: " . $param['createdBy'] . "%'";
}
$sql .= ' group by id.table1 ';
$rs = Query::RunQuery($sql, $arr);
foreach ($rs as $row) {
$records = new Account();
$results[] = $records;
}
return $results;
}
}
public static function formattedData($serArray, $orderBy = "giftcardaccount_id desc", $offset = 0, $limit = 10)
{
$arr = array();
if (is_array($param)) {
$i = 0;
$sql = "
select *
from table1
where
";
if (isset($param['startDate'])) {
$sql .= " date_created between ? AND ?";
$arr[] = $param['startDate'];
$arr[] = $param['endDate'];
$i++;
}
if (isset($param['amount']) && !empty($param['amount'])) {
if ($i > 0) $sql .= " AND ";
$sql .= " balance= ?";
$arr[] = $param['amount'];
$i++;
}
if (isset($param) && !empty($param['amount'])) {
if ($i > 0) $sql .= " AND ";
$sql .= " balance= ?";
$arr[] = $param['amount'];
$i++;
}
if (isset($param['createdBy']) && !empty($param['createdBy'])) {
if ($i > 0) $sql .= " AND ";
$sql .= " column2 like '%Created By: " . $param['createdBy'] . "%'";
}
$sql .= ' group by id.table1 ';
$rs = Query::RunQuery($sql, $arr);
return array("data" => $rs);
}
}
}
Why not have one method, but with an optional formatting options object/array?
public static function getData($params, $formatting = null) {
// continue as normal, adding formatting if it's there
}

SQL Multiple WHERE Clause Problem

I'm attempting the modify this Modx Snippet so that it will accept multiple values being returned from the db instead of the default one.
tvTags, by default, was only meant to be set to one variable. I modified it a bit so that it's exploded into a list of variables. I'd like to query the database for each of these variables and return the tags associated with each. However, I'm having difficulty as I'm fairly new to SQL and PHP.
I plugged in $region and it works, but I'm not really sure how to add in more WHERE clauses for the $countries variable.
Thanks for your help!
if (!function_exists('getTags')) {
function getTags($cIDs, $tvTags, $days) {
global $modx, $parent;
$docTags = array ();
$baspath= $modx->config["base_path"] . "manager/includes";
include_once $baspath . "/tmplvars.format.inc.php";
include_once $baspath . "/tmplvars.commands.inc.php";
if ($days > 0) {
$pub_date = mktime() - $days*24*60*60;
} else {
$pub_date = 0;
}
list($region, $countries) = explode(",", $tvTags);
$tb1 = $modx->getFullTableName("site_tmplvar_contentvalues");
$tb2 = $modx->getFullTableName("site_tmplvars");
$tb_content = $modx->getFullTableName("site_content");
$query = "SELECT stv.name,stc.tmplvarid,stc.contentid,stv.type,stv.display,stv.display_params,stc.value";
$query .= " FROM ".$tb1." stc LEFT JOIN ".$tb2." stv ON stv.id=stc.tmplvarid ";
$query .= " LEFT JOIN $tb_content tb_content ON stc.contentid=tb_content.id ";
$query .= " WHERE stv.name='".$region."' AND stc.contentid IN (".implode($cIDs,",").") ";
$query .= " AND tb_content.pub_date >= '$pub_date' ";
$query .= " AND tb_content.published = 1 ";
$query .= " ORDER BY stc.contentid ASC;";
$rs = $modx->db->query($query);
$tot = $modx->db->getRecordCount($rs);
$resourceArray = array();
for($i=0;$i<$tot;$i++) {
$row = #$modx->fetchRow($rs);
$docTags[$row['contentid']]['tags'] = getTVDisplayFormat($row['name'], $row['value'], $row['display'], $row['display_params'], $row['type'],$row['contentid']);
}
if ($tot != count($cIDs)) {
$query = "SELECT name,type,display,display_params,default_text";
$query .= " FROM $tb2";
$query .= " WHERE name='".$region."' LIMIT 1";
$rs = $modx->db->query($query);
$row = #$modx->fetchRow($rs);
$defaultOutput = getTVDisplayFormat($row['name'], $row['default_text'], $row['display'], $row['display_params'], $row['type'],$row['contentid']);
foreach ($cIDs as $id) {
if (!isset($docTags[$id]['tags'])) {
$docTags[$id]['tags'] = $defaultOutput;
}
}
}
return $docTags;
}
}
You don't add in more WHERE clauses, you use ANDs and ORs in the already existing where clause. I would say after the line $query .= " WHERE stv.name = '".$region... you put in
foreach ($countries as $country)
{
$query .= "OR stv.name = '{$country}', ";
}
but I don't know how you want the query to work.

Categories