Joomla - PHP altering mySQL statement in model - php

I'm having a syntax issue with this bit of code:
$query = "SELECT *
FROM ".$db->nameQuote('#__mls')."
WHERE 1=1"
. if ($zip != null)
{ AND ".$db->nameQuote('MSTZIP')." = ".$db->quote($zip)."}
. if ($city != null)
{ AND ".$db->nameQuote('MSTCITY')." = '".$db->quote($city)."'}
. if ($bdrms != null)
{ AND ".$db->nameQuote('MSTBDRMS')." >= ".$db->quote($bdrms)."}
. if ($bths != null)
{ AND ".$db->nameQuote('MSTBATHS')." >= ".$db->quote($bths)."}
. if ($lprice != null)
{ AND ".$db->nameQuote('MSTLISTPRC')." BETWEEN ".$db->quote($lprice)." AND ".$db->quote($hprice)."}
";"
;
First string " starts the query statement, second " layer assigns the table, when the WHERE statement, then it gets tricky. All the if statements are messing with me. I feel like that's where a " is getting misplaced or missing.

Utterly broken beyond belief. Try something more like this:
$query = "SELECT * FROM " . $db->nameQuote('#__mls') . " WHERE 1=1";
$clauses = array();
if ($zip != null) {
$clauses[] = $db->nameQuote('MSTZIP') . " = " . $db->quote($zip);
}
if (etc...) {
...
}
$query .= implode(' AND ', $clauses);
echo $query;

Before every AND you are missing " , you need to build your query in different manner (please double check every ' I am sure I missed few somewhere)
$query = "SELECT *
FROM ".$db->nameQuote('#__mls')."
WHERE 1=1";
if ($zip != null)
{ $query .= " AND ".$db->nameQuote('MSTZIP')." = '".$db->quote($zip)."'";}
if ($city != null)
{$query .= " AND ".$db->nameQuote('MSTCITY')." = '".$db->quote($city)."'";}
if ($bdrms != null)
{$query .= " AND ".$db->nameQuote('MSTBDRMS')." >= '".$db->quote($bdrms)."'";}
if ($bths != null)
{$query .= " AND ".$db->nameQuote('MSTBATHS')." >= '".$db->quote($bths)."'";}
if ($lprice != null)
{$query .= " AND ".$db->nameQuote('MSTLISTPRC')." BETWEEN '".$db->quote($lprice)." AND ".$db->quote($hprice).";}
;

Related

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()) {
...
}

Converting to PDO Parameter Binding

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?

Simple search feature, MySQL prepared statement issue

I'm currently in the process of creating a very simple search feature for a website in which the user is able to search a database for events using a number of different criteria (from one to many, varied number) and I'm experiencing an issue with the prepared statement I'm using, the bind_param() in particularly.
Here is the relevant PHP code:
...
...
$title = (empty($_POST['eventTitle'])) ? null : $_POST['eventTitle'];
$venue = (empty($_POST['venue'])) ? null : $_POST['venue'];
$catID = (empty($_POST['catID'])) ? null : $_POST['catID'];
$start = (empty($_POST['start'])) ? null : $_POST['start'];
$end = (empty($_POST['end'])) ? null : $_POST['end'];
$price = (empty($_POST['price'])) ? null : $_POST['price'];
include 'database_conn.php';
$sql = 'SELECT eventID, eventTitle, venueID, catID, eventStartDate,
eventEndDate, eventPrice FROM te_events WHERE 1';
$sqlCondition = '';
$bindFirstArg = '"';
$bindSecondArg = '';
if($title !== null && !empty($title)) {
$sqlCondition = $sqlCondition . " AND eventTitle LIKE \"%"
. $title . "%\"";
}
if($venue !== null && $venue !== '0') {
$sqlCondition = $sqlCondition . " AND venueID=?";
$bindFirstArg = $bindFirstArg . "s";
$bindSecondArg = $bindSecondArg . ", " . $venue;
}
if($catID !== null && $catID !== '0') {
$sqlCondition = $sqlCondition . " AND catID=?";
$bindFirstArg = $bindFirstArg . "s";
$bindSecondArg = $bindSecondArg . ", " . $catID;
}
if($start !== null && $start !== '0') {
$sqlCondition = $sqlCondition . " AND eventStartDate=?";
$bindFirstArg = $bindFirstArg . "s";
$bindSecondArg = $bindSecondArg . ", " . $start;
}
if($end !== null && $end !== '0') {
$sqlCondition = $sqlCondition . " AND eventEndDate=?";
$bindFirstArg = $bindFirstArg . "s";
$bindSecondArg = $bindSecondArg . ", " . $end;
}
if($price !== null && !empty($price)) {
$sqlCondition = $sqlCondition . " AND eventPrice=?";
$bindFirstArg = $bindFirstArg . "i";
$bindSecondArg = $bindSecondArg . ", " . $price;
}
$sql = $sql . $sqlCondition;
$bindFirstArg = $bindFirstArg . '"';
$search_stmt = $conn -> prepare($sql);
if (false===$search_stmt) {
die('prepare() failed: ' . htmlspecialchars($conn->error));
}
$search_stmt -> bind_param($bindFirstArg, $bindSecondArg);
$search_stmt -> execute();
$search_stmt -> bind_result($eventIDRes, $eventTitleRes, $venueIDRes,
$catIDRes, $eventStartRes, $eventEndRes, $eventPriceRes);
while ($search_stmt->fetch()) {
printf ("%s %s %s %s %s %s %i\n", $eventIDRes, $eventTitleRes,
$venueIDRes, $catIDRes, $eventStartRes, $eventEndRes, $eventPriceRes);
}
mysqli_stmt_close($search_stmt);
The error I'm receiving states
Warning: mysqli_stmt::bind_param() [mysqli-stmt.bind-param]: Number of elements in type definition string doesn't match number of bind variables in /var/www/vhosts/numyspace.co.uk/web_users/home/~unn_w12019212/public_html/webdev/searchresult.php on line 101"
Any ideas?
You need to pass bind_param a separate argument for each ? in your query, as well as the formats as the first parameter. You can't pass it a comma-separated string, that won't work. It just reads that as the first ? and then complains that you didn't send it the rest.
Also, don't add quotes inside your $bindFirstArg string. bind_param just wants a list of all the data types (i, d, s, or b) , it doesn't want " characters.
What you need to do is push your values into an array, then call bind_param via call_user_func_array.
$sqlCondition = '';
$bindFirstArg = '';
$bindParams = array();
// You need to bind $title as well, otherwise you are wide open to SQL
// injection and have just thrown out the benefits of prepared statements
if($title !== null && !empty($title)) {
$sqlCondition .= " AND eventTitle LIKE ?";
$bindFirstArg .= "s";
// Add the `%` to the value, not the query
$title = "%{$title}%";
// bind_param wants these to be references
$bindParams[] =& $title;
}
// Change all your ifs to look like this.
// They need to push into the $bindParams array
if($catID !== null && $catID !== '0') {
$sqlCondition .= " AND catID=?";
$bindFirstArg .= "s";
// bind_param wants these to be references
$bindParams[] =& $catID;
}
// etc...
$sql .= $sqlCondition;
$search_stmt = $conn->prepare($sql);
// Call bind_param with the correct number of parameters
array_unshift($bindParams, $bindFirstArg);
// This will make sure the parameters are passed correctly.
// Each variable needs to be passed as a separate parameter
call_user_func_array(array($search_stmt, 'bind_param'), $bindParams);
$search_stmt->execute();
$search_stmt->bind_result($eventIDRes, $eventTitleRes, $venueIDRes,
$catIDRes, $eventStartRes, $eventEndRes, $eventPriceRes);
while ($search_stmt->fetch()) {
printf ("%s %s %s %s %s %s %i\n", $eventIDRes, $eventTitleRes,
$venueIDRes, $catIDRes, $eventStartRes, $eventEndRes, $eventPriceRes);
}
$search_stmt->close();

php mysql adding values to array based on IF statements

Using Joomla, having issues trying to build a mySQL query based on URL arguments. The code I have looks like this:
$db =& JFactory::getDBO();
$hpprice = JRequest::getVar('hprice');
$lprice = JRequest::getVar('lprice');
$city = JRequest::getVar('city');
$zip = JRequest::getVar('zip');
$bdrms = JRequest::getVar('bdrms');
$bths = JRequest::getVar('bths');
$query = "SELECT * FROM " . $db->nameQuote('#__mls') . " WHERE 1=1";
$clauses = array();
if ($zip != null) {
$clauses[] = $db->nameQuote('MSTZIP') . " = " . $db->quote($zip);
}
if ($city != null) {
$clauses[] = $db->nameQuote('MSTCITY') . " = '" . $db->quote($city) . "'";
}
if ($bdrms != null){
$clauses[] = $db->nameQuote('MSTBDRMS')." >= ".$db->quote($bdrms);
}
if ($bths != null){
$clauses[] = $db->nameQuote('MSTBATHS') . " >= " . $db->quote($bths);
}
if ($lprice != null){
$clauses[] = $db->nameQuote('MSTLISTPRC') . " BETWEEN " . $db->quote($lprice) . " AND " . $db->quote($hprice);
}
$query .= implode(" AND ", $clauses);
$db->setQuery($query);
$table = $db->loadRowList();
return $table;
So, as you can see it, adds arguments to the mySQL query based on whether or not arugments exist in the URL. What I can't wrap my head around is building the array and imploding it.
Whenever I put an argument in the URL, all the table items populate. When I try to pass an argument, it comes up null. You can see this in action here. If you add another argument like zip to the URL, everything comes up NULL.
I think the problem is this " WHERE 1=1".try to change this to this-" WHERE 1=1 ".
Because the final query will be appended to this and you'll not get the desire result.For confirmation also echo $query see if it's a correct query.one more thing is '" . $db->quote($city) . "'".remove '' as you are already adding this by a function.
//Update:
Better to use where method
Let me know if this does not work.
Not sure how this is diffent than your previous question.
$query->select('*");
$query->from($db->nameQuote('#__mls'));
$query->where('1 = 1', AND);
if ($zip != null)
{
$query->where (.$db->nameQuote('MSTZIP')." = ".$db->quote($zip));
}
if ($city != null)
{
$query->where($db->nameQuote('MSTCITY')." = '".$db->quote($city));
}
Etc
There is no need for you to build any array; that it the whole point of having a databasequery api.
Final script ended up looking like this:
$db =& JFactory::getDBO();
$hprice = JRequest::getVar('hprice');
$lprice = JRequest::getVar('lprice');
$city = JRequest::getVar('city');
$zip = JRequest::getVar('zip');
$bdrms = JRequest::getVar('bdrms');
$bths = JRequest::getVar('bths');
$query = "SELECT * FROM " . $db->nameQuote('#__mls') . " WHERE 1=1 AND ";
$clauses = array();
if ($zip != null) {
$clauses[] = "MSTZIP = " . $zip;
}
if ($city != null) {
$clauses[] = "MSTCITY = " . $db->quote($city);
}
if ($bdrms != null){
$clauses[] = "MSTBDRMS >= " . $bdrms;
}
if ($bths != null){
$clauses[] = "MSTBATHS >= " . $bths;
}
if ($lprice != null){
$clauses[] = "MSTLISTPRC BETWEEN " . $lprice . " AND " . $hprice;
}
$query .= implode(" AND ", $clauses) . ";";
$db->setQuery($query);
$table = $db->loadRowList();
return $table;
I ended up getting rid of nameQuote and quote except where needed/applicable. The script model I was working off of was from some tutorial. It worked for what I was doing previously, but not now for some reason. Last step would be to make the initial AND conditional, but that shouldn't take much. At least the framework is there now. Thanks to all who helped.

PHP based code for filtering mysql results, can I make this more efficient?

I've made this php code for filtering the results from a mysql database. It works very well, but I'm sure this is not the most efficient way (or proper use of the language) to achieve the desired results. I'm trying my best to get "good" at writing code and would appreciate some feedback on how I could do this better.
$filter = "";
if (isset($_POST['submit']))
{
$aircraft_reg = "";
$prefix = "";
$part_number = "";
$flight_control = "";
if(!empty($_POST['aircraft_reg']))
{
$aircraft_reg = "aircraft_reg = '" . $_POST['aircraft_reg'] . "'";
}
if(!empty($_POST['prefix']))
{
$prefix = "prefix = '" . $_POST['prefix'] . "'";
}
if(!empty($_POST['part_number']))
{
$part_number = "part_number = '" . $_POST['part_number'] . "'";
}
if(!empty($_POST['flight_control']))
{
$flight_control = "flight_control = '" . $_POST['flight_control'] . "'";
}
if ($aircraft_reg != "" && ($prefix != "" || $part_number != "" || $flight_control != ""))
{
$a = " AND ";
}
else
{
$a = "";
}
if ($prefix != "" && ($part_number != "" || $flight_control != ""))
{
$b = " AND ";
}
else
{
$b = "";
}
if ($part_number != "" && $flight_control != "")
{
$c = " AND ";
}
else
{
$c = "";
}
if ($aircraft_reg != "" || $prefix != "" || $part_number != "" || $flight_control != "")
{
$filter = "WHERE " . $aircraft_reg . $a . $prefix . $b . $part_number . $c . $flight_control;
}
}
$result = mysql_query("SELECT * FROM installed $filter ORDER BY aircraft_reg , part_number, date_installed ASC");
You only need follow this pattern:
$result = mysql_query("
SELECT *
FROM installed
WHERE
".($_POST['aircraft_reg']?"aircraft_reg=" .mysql_real_escape_string($_POST['aircraft_reg']):"1" )." AND
...
ORDER BY aircraft_reg , part_number, date_installed ASC");
another alternative:
foreach($_POST as $key => $val)
if($key!="submit" and $val)
$filters[] = "$key='".mysql_real_escape_string($val)."' ";
$result = mysql_query("
SELECT *
FROM installed
".(isset($filters)?"WHERE ".implode("AND ",$filters):"")."
ORDER BY aircraft_reg , part_number, date_installed ASC");
I suggest you using something well-established such as ActiveRecord:
http://www.phpactiverecord.org/
No need to re-invent the wheel (unless this is purely for learning, in which case, carry on!)
... in the case this is purely for learning, don't forget to escape any REQUEST data such as those $_POSTs that you're using, with something like mysql_real_escape_string
Quick:
Use array_key_exists to see if something is in $_POST
Do not put $_POST values directly in your SQL, escape them. More info when you Google for SQL injection attack
I would validate/sanitize your input first, and then create the query in one go:
if (array_key_exists("partnumber", $_POST) {
$part_number = validate_partnumber($_POST['partnumber']);
$part_number = escape_for_db($part_number);
}
$q = ".... WHERE part_number='$part_number' ....";
Other than that, it doesn't look too bad.
You can try this, as conditional operator has less time complexity than if()-else(). Moreover less use of variables will cause less memory allocation, hence it is faster and more optimized than the one you used.
Another thing, using mysql_real_escape_string() prevent sql injection.
$filter = "";
if (isset($_POST['submit']))
{
$condition_count = 0;
if(!empty($_POST['aircraft_reg']))
{
$filter = " WHERE aircraft_reg = '" . mysql_real_escape_string($_POST['aircraft_reg']) . "'";
$condition_count++;
}
if(!empty($_POST['prefix']))
{
$condition_count > 0?$filter .= " AND prefix = '" . mysql_real_escape_string($_POST['prefix']) . "'":$filter .= " WHERE prefix = '" . mysql_real_escape_string($_POST['prefix']) . "'";
$condition_count++;
}
if(!empty($_POST['part_number']))
{
$condition_count > 0?$filter .= " AND part_number = '" . mysql_real_escape_string($_POST['part_number']) . "'":$filter .= " WHERE part_number = '" . mysql_real_escape_string($_POST['part_number']) . "'";
$condition_count++;
}
if(!empty($_POST['flight_control']))
{
$condition_count > 0?$filter .= " AND flight_control = '" . mysql_real_escape_string($_POST['flight_control']) . "'":$filter .= " WHERE flight_control = '" . mysql_real_escape_string($_POST['flight_control']) . "'";
$condition_count++;
}
}
$result = mysql_query("SELECT * FROM installed ".$filter." ORDER BY aircraft_reg , part_number, date_installed ASC");
if (!isset($_POST['submit'])) exit;
$aircraft_reg = $_POST['aircraft_reg'];
$prefix = $_POST['prefix'];
$part_number = $_POST['part_number'];
$flight_control = $_POST['flight_control'];
$result = mysql_query("
SELECT *
FROM installed
where
aircraft_reg = if('$aircraft_reg' = '', aircraft_reg, '$aircraft_reg')
and
prefix = if('$prefix' = '', prefix, '$prefix')
and
part_number = if('$part_number' = '', part_number, '$part_number')
and
flight_control = if('$flight_control' = '', flight_control, '$flight_control')
ORDER BY aircraft_reg , part_number, date_installed
");
If this is for real then don't forget to sanitize the user input or you will be an easy sql injection victim.

Categories