How to safely use possibly unsafe data with Query::select()? - php

First all, sorry about my english!
I'm trying do a dynamic select in CakePHP when i need change the statements of search. I know can use a variable to do that, but i think about SQL injection. It has other way to do this ?
Example:
$var = "quantidade";//$var can be other values.
$query->find('')->select('quantidade' => $var);

Unlike in other places like Query::where() when using the key => value syntax, there are no SQL injection prevention mechanisms in Query::select(), values passed to this method are being inserted into the query as is (they might get quoted, but not escaped), so you have to take care of securing this yourself.
I'd suggest to use a whitelist, either a hardcoded one:
$allowedFields = [
'field_a',
'field_b'
];
if (in_array($var, $allowedFields, true)) {
$query = $Table
->find()
->select([
'alias' => $var
]);
}
or one retrieved from the tables schema in case you want to allow all fields, but be careful, only do this if it's definitely safe to expose all possible columns to the user!
$allowedFields = $Table->getSchema()->columns(); // use schema() in CakePHP < 3.4
See also
Cookbook > Database Access & ORM > Query Builder > SQL Injection Prevention
Cookbook > Database Access & ORM > Schema System
API > \Cake\ORM\Table::getSchema()
API > \Cake\ORM\Table::schema()

Related

Add One to Integer Column Using Query in CakePHP 3

I dont recall exactly how, but I do remember being able to add one to an integer column in mysql with a query without knowing the value of that integer column...
something like
update table set column=column+1 where id=1
I am wondering, how can I do the above query with CakePHP 3
You can use \Cake\ORM\Table::updateAll() (or the underlying \Cake\Database\Query::update()), and use an expression to generate the arithmetic operation part, something like:
$additionExpression = $Table->query()->newExpr('column + 1');
$affectedRows = $Table->updateAll(
['column' => $additionExpression],
['id' => 1]
);
Or a little more complex in case you want to make use of automatic identifier quoting:
$additionExpression = $Table
->query()
->newExpr()
->add([
new \Cake\Database\Expression\IdentifierExpression('column'),
'1'
])
->setConjunction('+'); // tieWith() before CakePHP 3.4
See also
Cookbook > Database Access & ORM > Saving Data > Bulk Updates
Cookbook > Database Access & ORM > Query Builder > Raw Expressions

Failed to check the values has addslashes values in mysql

I have stored the datas in db with the addslashes when i submit the form.
Im stored values using the function like below
addslashes(trim($data[1]));
I want to check existing record in that table but its not working when it has value like
Regional Sales Director - Americas\'
Its checking existing values in table without those shlashes
\'
My query is
$query = $this->db->query("select * from tbl_contacts where contact_name='".$name."' and contact_company='".$company."' and contact_designation='".$designation."'");
$result1 = $query->result();
I'm not sure i should answer to this, but i feel i should because what you are doing is wrong in so many ways...
You should never ever do things like that if you want to insert data - especially if you use a framework which can do the job for you...
First of all you've to understand how Codeigniter inserts Data
Use the query builder
an example for inserting data would be
$arrData = [
'contact_name' => $this->input->post('contact_name'),
'contact_company' => $this->input->post('contact_company')
];
$this->db->insert('tbl_contacts', $arrData);
please read carefully the section in the CI Documentation here
and your select query is a disaster because you don't protect anything - you are widely open to any sort of attacks as Alex already said in the comments
Instead you should try the following:
$query = $this->db
->select('*')
->from('tbl_contacts')
->where('contact_name', $name)
->where('conatct_company', $company)
->where('contact_designation', $designation)
->get();
$result1 = $query->result();
Furthermore, please study the documentation, below are some links which are mandatory
Form Validation
Security Class
Input Class
Query Builder Class
If you've accidentally escaped your data with addslashes on top of existing database escaping: you may be able to methodically remove those back slashes with an update and replace to fix your data.
UPDATE tableName
SET columnName = replace(columnName, '\\', '');
But do be very careful, back up all your data first and test on a sample.
Then in the future, do not use addslashes on top of your database library's escape mechanism for updates or inserts.

Cakephp3 case mysql statement is not creating the correct query

I'm trying to create a query that returns the sum of a column using a case (it has logged time and the format in either minutes or hours, if it's in hours, multiply by 60 to convert to minutes). I'm very close, however the query is not populating the ELSE part of the CASE.
The finder method is:
public function findWithTotalTime(Query $query, array $options)
{
$conversionCase = $query->newExpr()
->addCase(
$query->newExpr()->add(['Times.time' => 'hours']),
['Times.time*60', 'Times.time'],
['integer', 'integer']
);
return $query->join([
'table' => 'times',
'alias' => 'Times',
'type' => 'LEFT',
'conditions' => 'Times.category_id = Categories.id'
])->select([
'Categories.name',
'total' => $query->func()->sum($conversionCase)
])->group('Categories.name');
}
The resulting query is:
SELECT Categories.name AS `Categories__name`, (SUM((CASE WHEN
Times.time = :c0 THEN :c1 END))) AS `total` FROM categories Categories
LEFT JOIN times Times ON Times.category_id = Categories.id GROUP BY
Categories.name
It's missing the ELSE statement before the CASE end, which according to the API docs:
...the last $value is used as the ELSE value...
https://api.cakephp.org/3.3/class-Cake.Database.Expression.QueryExpression.html
I know there might be a better way to do this, but at this point I'd like to at least know how to do CASE statements properly using the built in QueryBuilder.
Both arguments must be arrays
Looks like there are some documenation issues in the Cookbook, and the API could maybe be a little more clear on that subject too. Both, the $conditions argument as well as the $values argument must be arrays in order for this to work.
Enforcing types ends up with casting values
Also you're passing the SQL expression wrong, including the wrong types, defining the types as integer will cause the data passed in $values to be casted to these types, which means that you will be left with 0s.
The syntax that you're using is useful when dealing with user input, which needs to be passed safely. In your case however you want to pass hardcoded identifiers, so what you have to do is to use the key => value syntax to pass the values as literals or identifiers. That would look something like:
'Times.time' => 'identifier'
However, unfortunately there seems to be a bug (or at least an undocumented limitation) which causes the else part to not recognize this syntax properly, so for now you'd have to use the manual way, that is by passing proper expression objects, which btw, you may should have done for the Times.time*60 anyways, as it would otherwise break in case automatic identifier quoting is being applied/required.
tl;dr, Example time
Here's a complete example with all forementioned techniques:
use Cake\Database\Expression\IdentifierExpression;
// ...
$conversionCase = $query
->newExpr()
->addCase(
[
$query->newExpr()->add(['Times.time' => 'hours'])
],
[
$query
->newExpr(new IdentifierExpression('Times.time'))
->add('60')
->tieWith('*'), // setConjunction() as of 3.4.0
new IdentifierExpression('Times.time')
],
);
If you were for sure that you'd never ever make use of automatic identifier quoting, then you could just pass the multiplication fragment as:
'Times.time * 60' => 'literal'
or:
$query->newExpr('Times.time * 60')
See also
Cookbook > Database Access & ORM > Query Builder > Case statements
Cookbook > Database Access & ORM > Query Builder > Using SQL Functions
API > \Cake\Database\Expression\QueryExpression::add()
API > \Cake\Database\Expression\QueryExpression::tieWith()

Doctrine queryBuilder: SQL Injection risk in addOrderBy() method?

I a using Doctrine in a Symfony 2.8 project and I wonder if there is any risk of SQL Injections when using the addOrderBy() method of the queryBuilder:
// Order options. Real code does not specify this manually, but receives
// the options via user form input
$orderBy' = array(
'column1' => 'ASC',
'column2' => 'DESC',
...
'columnN' => 'ASC',
);
$qb = $this->em->createQueryBuilder();
...
foreach ($orderBy as $column => $orderOption) {
$qb->addOrderBy("e.$column", $orderOption);
// Does not work:
// $qb->addOrderBy("e.$column", ':orderOption')
// ->setParameter('orderOption', $orderOption);
//
// Error: Expected end of string, got ':orderOption'"
}
// Result is something like:
...ORDER BY e0_.column1 ASC, e0_.column2 DESC...
The problem is, that the order options are received via user form input that could be manipulated to something like ; DROP TABLE someTable instead of ASC or DESC.
I already tried this, but the query builder doesn't seem to accept multiple queries separated by ;, which does not mean, that there could not be any other/better injections :-)
Of course the problem could easily be solved by filtering the received results and skip all invalid search options. But I am trying to understand, if the addOrderBy() method in general. Is it save to pass any value to the method and Doctrine will handle the rest, or is there a potential risk?
I wonder why the ->setParameter() method does not work, as it would when using ->where().
Short answer is that column names submitted by form could in fact be used for a sql injection attack. Doctrine assumes you have properly validated column (and table) names.
The doctrine code is fairly easy to read and it's worth taking a look at for these sorts of questions:
public function addOrderBy($sort, $order = null)
{
$orderBy = ($sort instanceof Expr\OrderBy) ? $sort : new Expr\OrderBy($sort, $order);
return $this->add('orderBy', $orderBy, true);
}
Note that there is no value at all in using Expr in your queries. Doctrine takes care of generating them for you.
$this->add is a bit more complicated but basically the second argument ends up being passed along with no escaping or filtering etc.
I wonder why the ->setParameter() method does not work, as it would
when using ->where()
The important concept is that prepared statements only protect values not column or table names.
So again, it entirely up to you to filter table/column names coming from the wild. And looking at the source can be informative.
You can use the Expr class: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html#the-expr-class
Or a simple function/method to return a valid value:
function orderOption($option, $defaultOption = 'ASC') {
if (in_array(strtoupper($option), ['ASC', 'DESC']) {
return $option;
}
return $defaultOption;
}

YII bunch inserting - avoiding SQL-injection

I insert big chunks of data to DB (~ 500) in the loop ( there are nearly 20000 or more records in total):
$builder = Yii::app()->db->schema->commandBuilder;
$command = $builder->createMultipleInsertCommand('product_supplier',
$dataToDb
);
$command->execute();
Using AR one can use validate() method to ensure that data are valid and AFAIK model escapes all dangerous data.
I would like to avoid to be SQL-injected.
Should I escape all data on my own when I use multiple insert or Yii takes care about it ?
Is it good idea to use standard PHP function "mysqli_escape_string " ?
I feel unsure how good it is.
Thanks.
The CDbCommand::createMultipleInsertCommand() method uses param binding, so it's safe.
ActiveRecords also use param binding and there's no extra escaping as it is not required.

Categories