YII bunch inserting - avoiding SQL-injection - php

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.

Related

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.

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;
}

how to get the where clause in string format using CakePHP3 ORM?

In CakePHP3, there is a ORM that helps with building queries.
From the documentation, I can see that
$query = $articles->find(); // build a query that has not run yet
$query->where(['id' => 1]); // Return the same query object
So in this case, I want the string
WHERE `articles`.`id` = 1
After much googling, I found out that there is a way to return just the where clause of a query object.
$query->where(['id' => 1])->clause('where'); // Return the where clause in the form of a QueryExpression
More googling leads me to find out how to get the QueryExpression to spit out string representation
$query->where(['id' => 1])->clause('where')->sql($valueBinder); // Return the where clause in string format
Here is my problem. I don't know what the $valueBinder is supposed to look like. I don't know how to initialize it.
I am also happy not to use ValueBinder as long as I can get the where clause in string format using CakePHP 3 ORM and in the right SQL dialect. Please assume I am using MySQL.
Please advise.
EDIT
I tried to use $query->valueBinder() as the $valueBinder.
It is empty and does not contain the associated c:0 to the value 1.
To directly answer your question, you can get the SQL for any clause this way:
$binder = new \Cake\ORM\ValueBinder();
$query->clause('where')->sql($binder);
That will return the SQL with the correct placeholders, not with the values to be used. The values live in the $binder variable and are used for statement objects.
As I can see, you only wanted to preserve the internal structure of the where clause to pass it to another query in a different request. Your solution is fine, but I'd like to add that you can also encode a full conditions tree from an existing query:
$where = serialize($query->clause('where'));
$anotherQuery->where(unserialize($where)); // A query in another request
In any case, you need to be careful with what you are unserializing as taking it directly from user input will certainly lead to security problems.
You can choose to omit this param if you like. Please see http://api.cakephp.org/3.0/class-Cake.Database.Query.html#_sql
In addition, you can use the Query member function traverse($visitor, $parts) to isolate the where clause. $visitor is a function that takes a value and a clause. You define the behavior of $visitor. $parts is an array of clause names. I suggest passing array('where') into this param.
My workaround is that I store the conditions in json string format.
Using the same example, what I do is
$data['conditions'] = json_encode(['Articles.id' => 1]); // encode into JSON string
$this->DynamicRules->patchEntity($dynamicRule, $data); // use in edit action of DynamicRulesController
then when I need to reuse the conditions, I do:
$articlesTable = TableRegistry::get('Articles');
$query = $articlesTable->find(); // new query for Articles
$rule = json_decode($dynamicRule->conditions, true); // get back the conditions in associative array format
$query->where($rule); // re-assign the conditions back
This got me what I ultimately wanted.

JSON from SQL Table get double result

first of all i have to tell you that it is my first step on php and JSON.
I decided to use JSON to get value from a customer SQL Table.
I get my results using this script :
mysql_connect($config['mysql_host'],$config['mysql_user'],$config['mysql_pass']);
//select database
#mysql_select_db($config['db_name']) or die( "Unable to select database");
mysql_query('SET CHARACTER SET utf8');
$fet=mysql_query('select * from vehicule');
$json = array();
while($r=mysql_fetch_array($fet)){
$json[] = $r;
}
header('Content-Type: application/json');
echo $json_data=json_encode($json);
Everything is ok, exept that my JSON results looks like :
0 = 462;
1 = "Hyundai ix20 crdi 115 panoramic sunsation";
10 = 1346450400;
11 = "462-Hyundai-ix20-crdi-115-panoramic-sunsation";
12 = 462;
...
id = 462;
kilometrage = 14400;
marque = 4;
modele = 137;
motorisation = 2;
ordre = 462;
prix = 17500;
puissance = 6;
titre = "Hyundai ix20 crdi 115 panoramic sunsation";
url = "462-Hyundai-ix20-crdi-115-panoramic-sunsation";
...
I have result of the table in 2 versions : one with 0:value, 1:value, 2... and the other one using the table key, how can i print only the second one ?
By the way can someone give me link so i can know by what i have to replace mysql which is think out of date ? (i'm a beginner few hours using PHP)
Thank you very much !
You have two different issues happening here. One is outright causing the issue you are seeing, and the other is a bad practice mistake that will leave you wide open for trouble in the long run.
The first issue is the one you're asking about. The mysql_fetch_array function (see the Docs here) expects a minimum of one input (the result input) that you are providing. It also has a second, optional input. That optional input defaults to MYSQL_BOTH, which returns an associative array with the results available both through keys (column names) and their indexes. Which is to say, that if you select the column 'id', you get it's value in both $array[0] and $array['id']. It's duplicated, and thus the JSON process carries over the duplication. You need to provide a second value to the function, either MYSQL_ASSOC to get $array['id'] or MYSQL_NUM to get $array[0].
Your second issue is the choice of functions. You're using the 'raw' mysql functions. These have been depreciated, which is a technical term that means 'these functions are no longer supported, but we've left them in to give you time to fix legacy code'. For legacy, read 'old'. Those functions will be going away soon, and you need to upgrade to a better option -- either the mysqli functions, or the PDO class. I strongly recommend the PDO class, as once you learn it it's easy to learn and has the advantage of being more portable. Whichever set you go with, you need to learn to use prepared statements as both a performance and security issue. Right at the moment, you're working with 'raw' statements which have a history of being very easy to interfere with via what's called an 'injection attack'. You can see a fictionalized example of such an attack here, and there are plenty of articles online about it. These attacks can be incredibly complex and difficult to fight, so using prepared statements (which handle it for you), is strongly recommended. In the specific example you're using here, you don't need to worry about it because you aren't including any user inputs, but it's an important habit to get into.

Mysql - PHP different values searches

I know how to perform mysql searches using for example the WHERE word. But my problem is that i need to search on different values, but these can vary in number. For example:
I can search for 3 variables Name, LastName, Age
BUT
I in other search, i can look for 2 variables Name, Age.
Is there a way to perform a MYSQL search with the same script, no matter the quantity of values i search.??
Ot it is a better practice to "force" the search of a fixed amount of variables.??
Thanks.!
Roberto
IMHO, it is far better to limit the search to a fixed number of variables. That way you are answering a specific question for a specific reason, not trying to fit a general answer to your specific question. Limiting the search criteria makes the statement(s) easier to debug and benchmark for performance.
Hope this helps.
Just use a variable for your search parameters and inject that into your query. Just ensure that in the function/method you put the variable into the proper format (which will depend on how you select the different values.)
SELECT *
FROM db
$variable;
There will be no WHERE clause seen, unless it is passed your values (meaning you can use this same query for a general search of the db) without fear of having an empty/required $variable.
Your $variable when constructed would need to have to have the WHERE clause in it, then each value you add, insert it (in a loop perhaps) in the proper format.
Hope this makes sense, if not let me know and I will try to clarify. This is the same method most people use when paginating (except they put the variable in the LIMIT instead of the WHERE)
EDIT:
Also make sure to properly sanitize your variable before injection.
Simple example of dynamically building a query:
$conditions = array();
if (...) {
$conditions['age'] = $age;
}
if (...) {
$conditions['name'] = $name;
}
...
if (!$conditions) {
die('No conditions supplied');
}
// if you're still using the mysql_ functions and haven't done so before:
$conditions = array_map('mysql_real_escape_string', $conditions);
foreach ($conditions as $field => &$value) {
$value = "`$field` = '$value'";
}
$query = 'SELECT ... WHERE ' . join(' AND ', $conditions);
It's really not hard to dynamically cobble together the exact query you want to create. Just be careful you don't mess up the SQL syntax or open yourself to more injection vulnerabilities. You may want to look at database abstraction layers, which pretty much allow you to pass a $conditions array into a function which will construct the actual query from it, more or less the way it's done above.

Categories