Doctrine 1 allowing SQL Injection? - doctrine

I found this code on a legacy app:
$salt = $this->generateSalt();
$new_pass_update = Doctrine_Query::create()
->update('User')
->set('password', '"'. $this->hash($newPass, $salt) .'"')
->set('salt', "sleep(10)") // $salt) <- I replaced this
->where('email = ?', array($mail))
->getDql();
die($new_pass_update);
I was shocked to see this Dql generated as output:
UPDATE User SET password = "3dbe00a167653a1aaee01d93e77e730e"
salt = sleep(10) WHERE email = ?
First of all, I didn't expect to see the quotation marks around the password value. I thougt that Doctrine would do that for me, so I tried the second argument without them, but I was shocked to see this Dql generated as output:
UPDATE User SET password = "3dbe00a167653a1aaee01d93e77e730e"
salt = sleep(10) WHERE email = ?
If I change ->getDql() for -> execute() that's exactly the query that is executed and the db sleeps for 10 seconds.
Why is doctrine behaving like this?

As Gumbo pointed out, the right API to use with Doctrine 1.* update syntax is:
$new_pass_update = Doctrine_Query::create()
->update('User')
->set('password', "?", $this->hash($newPass, $salt))
->set('salt', "?", $salt)
->where('email = ?', array($mail))
->execute();
so, the second argument should be "?" and the third one, the associated value.

Related

appending to rails field value

I need to find and update a number of records in a Rails 3.2, Ruby 2 application. The following code successfully finds the records I want. What I need to do though is add " x" (including the space) to the email address of every user and I can't figure out how to do it.
This finds the records
User.joins(:account)
.where("users.account_id NOT IN (?)", [1955, 3083, 3869])
.where("accounts.partner_id IN (?)", [23,50])
.where("users.staff = '0'")
.where("users.admin = '0'")
.where("users.api_user = '0'")
.where("users.partner_id is null")
.update_all(email: :email.to_s << " X")
but it's the last line I'm having problems with. Is this possible, or do I need to find the records another way?
The update_all method updates a collection of records, but unless you write your own SQL expression, it can only set one value. For example, if you wanted to overwrite all the email addresses with "X", you could do it easily:
User.joins(:account)
.where("users.account_id NOT IN (?)", [1955, 3083, 3869])
# ...other scopes...
.update_all(email: "X")
In your case, what you really need to do is make individual updates to all these records. One way to do it is to find the records, then loop over them and update them one at a time:
users_to_update = User.joins(:account)
.where("users.account_id NOT IN (?)", [1955, 3083, 3869])
.where("accounts.partner_id IN (?)", [23,50])
.where("users.staff = '0'")
.where("users.admin = '0'")
.where("users.api_user = '0'")
.where("users.partner_id is null")
users_to_update.each do |user|
user.update_attribute(:email, "#{user.email} X")
end
Another solution would be to use a SQL expression with update_all, as in Zoran's answer.
Try writing the last line like so:
.update_all("email = email || ' X'")
This uses SQL's string concatenation operator to append the X to the end of the emails.
Hope that helps!

CodeIgniter Active Record Where Not In String

I'm having an issue with CI Active Record's "Where Not In". I am trying to exclude a series of ID's. I couldn't understand why everything worked fine and dandy with one record, but not with multiple.
My Query
$this->db->where_not_in('crm.user_id', $ignore);
The problem is when I profile the the Query is wrong.
With a string of ID's
// $ignore = "12,13";
SELECT *
FROM (`crm`)
WHERE `crm`.`user_id` NOT IN ('16,13')
AND `survey` = 1
With a string of Quotes ID's
// $ignore = "'12','13'";
SELECT *
FROM (`crm`)
WHERE `crm`.`user_id` NOT IN ('\'16\',\'13\'')
AND `survey` = 1
Am I forced to do a loop of "or_where_not_in" or something like that?
where_in and where_not_in expect you to pass an array, not a string as the 2nd parameter.
$ignore = array(12, 13);
$this->db->where_not_in('crm.user_id', $ignore);
Link to the docs: http://www.codeigniter.com/userguide2/database/active_record.html

Using OR in CodeIgniter Active Record's Delete Method

I need to be able to convert the following to Ci's active record delete method but I don't know how to use the OR in the delete statement. Could you please tell me how I'd do this correctly?
$this->db->query("DELETE FROM friend WHERE userid_friends = '{$userid}' AND friendId_friends = '{$targetedUserId}' OR userid_friends = '{$targetedUserId}' AND friendId_friends = '{$userid}' ");
I guess your trying this:
$this->db->query("DELETE FROM friend WHERE
( userid_friends = '{$userid}' AND friendId_friends = '{$targetedUserId}') OR
( userid_friends = '{$targetedUserId}' AND friendId_friends = '{$userid}') ");
(note the added parentheses for the two AND clauses)
But actually your not using CI's "DELETE" method just a query.
Using active record delete would be something like:
$this->db->where("userid_friends = '{$userid}' AND friendId_friends = '{$targetedUserId}'");
$this->db->or_where("userid_friends = '{$targetedUserId}' AND friendId_friends = '{$userid}'");
$this->db->delete('friend');
For debugging of complex queries I recommend you to use
echo $this->db->last_query();
As it shows you exactly how the final query was rendered by the Active record methods.

Weird backticks behaviour in Active Record in CodeIgniter 2.0.3

Previously my all queries were running fine in CI version 2.0 but when I upgraded to 2.0.3 some of my SELECT queries were broken.
CI is adding backticks (``) automatically, but in older version its running as it is.
CI user manual have instructed to add second parameter in
db->select
as
FALSE
but still it's not working.
Code is as following:
class Company_model extends MY_Model
{
----------------
$this->db->select(' count('.$fieldname. ') as num_stations');
$this->db->select(" CONCAT_WS(',', clb_company.address1, clb_company.address2, clb_company.city, clb_company.state, clb_company.zipcode ) as companyAddress");
$this->db->from($this->_table);
$this->db->join($this->_table_device, $fieldname1. " = ". $fieldname2, 'LEFT');
$this->db->where($blablafield , '0');
----------------
The error is as follows:
Error Number: 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
'FROM (`clb_device`) JOIN `clb_company` ON `clb_company`.`id` = `clb_device`.`com' at line 2
SELECT `clb_device`.`id` as deviceId, `clb_pricing_specifications`.`name` as pricingSpecName, `clb_company`.`name` as companyName, `clb_device`.`mac_address` as deviceMacAddress,
`clb_device`.`reseller_model_number` as deviceModelNumber, `clb_pricing_spec_grouping`.`pricing_master_spec_id` as pricingSpecId, `clb_device`.`address` as deviceAddress,
`clb_device`.`is_home` as deviceIsHomeCharger, CONCAT(clb_company.portal_line1, `'/'`, `clb_device`.`name)` as deviceDisplayName FROM (`clb_device`) JOIN `clb_company`
ON `clb_company`.`id` = `clb_device`.`company_id` LEFT JOIN `clb_pricing_group_devices` ON `clb_device`.`id` = `clb_pricing_group_devices`.`device_id` and clb_pricing_group_devices.is_active = 1
LEFT JOIN `clb_pricing_spec_grouping` ON `clb_pricing_group_devices`.`pricing_spec_id` = `clb_pricing_spec_grouping`.`pricing_master_spec_id` LEFT JOIN `clb_pricing_specifications` ON
`clb_pricing_spec_grouping`.`pricing_spec_id` = `clb_pricing_specifications`.`id` WHERE clb_company.vendor_id is not null AND cast(substr(clb_devi
ce.software_version, 1, 3) as decimal(2,1)) > 2.0 AND clb_device.device_state > 0 GROUP BY `clb_device`.`id` ORDER BY CONCAT(trim(clb_company.portal_line1), `'/'`, trim(clb_device.name)) desc LIMIT 20
Have a look at CONCAT(trim(clb_company.portal_line1), `'/'`, trim(clb_device.name))
Please suggest the workaround.
Use this line before your query:
$this->db->_protect_identifiers=false;
This will stop adding backticks to the built query.
The solution is very simple:
In the database configuration file (./application/config/database.php) add a new element to array with default settings.
$db['default']['_protect_identifiers']= FALSE;
This solution is working for me and more elegant and professional.
All other answers are really old, this one works with CI 2.1.4
// set this to false so that _protect_identifiers skips escaping:
$this->db->_protect_identifiers = FALSE;
// your order_by line:
$this -> db -> order_by('FIELD ( products.country_id, 2, 0, 1 )');
// important to set this back to TRUE or ALL of your queries from now on will be non-escaped:
$this->db->_protect_identifiers = TRUE;
class Company_model extends MY_Model
{
----------------
$this->db->select(" count('$fieldname') as num_stations",false);
$this->db->select(" CONCAT_WS(',', clb_company.address1, clb_company.address2, clb_company.city, clb_company.state, clb_company.zipcode ) as companyAddress",false);
$this->db->from($this->_table);
$this->db->join($this->_table_device, $fieldname1. " = ". $fieldname2, 'LEFT');
$this->db->where($blablafield , '0');
----------------
The false you were talking about is what is needed, can you try the code above and copy and paste to us the output of
echo $this->db->last_query();
This will show us what the DB class is creating exactly and we can see whats working / what isn't. It may be something else (you haven't given the error from that is generated sometimes sql errors can be misleading.)
From the docs:
$this->db->select() accepts an optional second parameter. If you set it to FALSE, CodeIgniter will not try to protect your field or table names with backticks. This is useful if you need a compound select statement.
CI will only protect your ACTIVE RECORD calls, so if you are running $this->db->query(); you will be fine, and based on the notes you should be safe with AD calls like so to disable backticks (not sure why you say they don't work, but I don't see your full code, so I can't be sure)
$this->db->select('(SELECT SUM(payments.amount) FROM payments WHERE payments.invoice_id=4') AS amount_paid', FALSE);
$query = $this->db->get('mytable');
make sure FALSE is without single quotes (makes it a string), and it might not validate (not tested by me).
I think you should check DB_driver.php file, there is a variable named as protect_identifier, the point is when you will check with older version of CI, you will see that there is a condition which is missing in new version,escape variable which is checked for nullability, paste that condition from older version and you will be OK
CI_DB_active_record::where() has a third param for escaping, this has worked better for me than switching on and off CI_DB_driver::_protect_identifiers
public function where($key, $value = NULL, $escape = TRUE)
Not sure what CI version this was added in.
HTH someone
Here's a trick that worked for me. Replace this line
$this->db->join($this->_table_device, $fieldname1. " = ". $fieldname2, 'LEFT');
with this:
$this->db->join($this->_table_device, $fieldname1. " IN(". $fieldname2 .")", 'LEFT');
this will prevent CI from escaping your field. It's not ideal but it's better than the alternatives.
I just read a simple solution for this...
I changed the value of var $_escape_char (system/database/drivers/mysql/mysql_driver.php, line 36..
It was
var $_escape_char = '`';
Changed to
var $_escape_char = ' ';
and now it works... But i am affraid if I made any security issues..
Thanks

Add a clause to a MySQL statement without quotes using CodeIgniter's Active Record functions

I wanted to update a row using active records in codeigniter, and I only want to increment a field value (received_qty = received_qty +1) , I realized that I can do that in usual sql, but I cant in codeigniter active records
$update['received_qty'] = 'received_qty + 1';
$update['received_date'] = date("Y-m-d");
$this->db->where($where);
$this->db->update('vrs_distribution', $update);
anyone know how to do it using active records?
This will work.
$this->db->set('received_qty', 'received_qty + 1', FALSE);
$this->db->set('received_date', date("Y-m-d"));
$this->db->where($where);
$this->db->update('vrs_distribution');
ActiveRecord escapes everything put into a set(), where() and many other methods. Where and set can both take an optional third parameter of $escape which is a boolean. Set it to FALSE and CodeIgniter will not escape anything, meaning your field increment wont be treated as a string.
Or you can do:
$this->db->query('UPDATE vrs_distribution SET received_qty = received_qty + 1, received_date = CURDATE() WHERE id = ' . $id);
You would need to modify WHERE clause to suit you though
status set to zero(Update)
$this->db->set('IsCurrent', '0');
$this->db->where('AcademicYearID',$academicYearId);
$this->db->update('academicyear');
I was going to ask a similar question just a little bit different, but the problem was the same: I needed to update a date with an interval (expiry_date = expiry_date + interval 3 month) and Phil Sturgeon's answer did solve the problem.
However, what I realized is that you can still use the array for the fields that can have quotes, meaning that you could write:
$this->db->set('received_qty', 'received_qty + 1', FALSE);
$this->db->set('expired_date', 'CURDATE() + INTERVAL 10 DAY', FALSE); //extra example 1
$update['received_date'] = date("Y-m-d");
$update['receiver'] = $receiver_name; //extra example 2
$this->db->where($where);
$this->db->update('vrs_distribution', $update);
Where $this->db->last_query() would output:
UPDATE `vrs_distribution`
SET `received_qty` = received_qty + 1,
`expiry_date` = CURDATE() + INTERVAL 10 DAY, #extra example 1
`received_date` = '2015-07-01 16:00:00',
`receiver` = 'strike_noir', #extra example 2
WHERE #where clause with backticks
Notice that the fields where set() was used do not have quotes and appear in the first place. The rest of the query has backticks (letting "CodeIgniter protect the remaining fields").
You seem pretty close, there isn't an 'increment by one' command in CI's ActiveRecord (or in SQL for that matter).
$update['received_qty']++;
$update['received_date'] = date("Y-m-d");
$this->db->where($where);
$this->db->update('vrs_distribution', $update);

Resources