Scope and Field as Parameter in Rails - ruby-on-rails

I've added a scope to a Rails model that allows for searching based on a specified parameter field using a range. Here is what it looks like:
scope :upcoming, lambda { |field|
where("to_char(#{field}, 'DDD') BETWEEN :alpha AND :omega",
alpha: Time.now.advance(days: 4).strftime('%j'),
omega: Time.now.advance(days: 8).strftime('%j'),
)
}
Event.upcoming(:registration) # Query all events with registration shortly.
Event.upcoming(:completion) # Query all events with completion shortly.
The above works fine, however in creating I read the Ruby on Rails Guides and found the following:
Putting the variable directly into the conditions string will pass the variable to the database as-is. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string.
Although the scope is currently never called with a user parameter, I am curious if a way exists of setting the field without using the interpolation in order to better conform with the above recommendation. I've tried using another named parameter, however this will escape the field with quotes (and thus cause it to fail). Any ideas?

I would recommend validating the field parameter against the model's attributes, essentially using the model as a whitelist for values that are allowed to be passed. Something like this:
scope :upcoming, lambda { |field|
if column_names.include? field.to_s
where("to_char(#{field}, 'DDD') BETWEEN :alpha AND :omega",
alpha: Time.now.advance(days: 4).strftime('%j'),
omega: Time.now.advance(days: 8).strftime('%j'),
)
else
# throw some error or return nil
end
}

Okay, reading all the way to the end might help(thanks rubyprince). It looks like you are doing a between query on a field that is storing a date in Oracle. The problem is that to_char is looking for a variable, not a string. And the act of escaping a variable in rails turns it into a string. So, in this particular case you might convert :alpha and :omega into the format of the value stored in field. That way you can escape field in a straightforward manner. Of course there is the issue with Oracle treating dates as Time. I'm guessing that is why you converted to day-of-year for the compare. If you are using the Oracle Enhanced Adaptor you can set
self.emulate_dates_by_column_name = true
to make sure that the field is treated like a date. Then use the to_date function(which takes a string) with :alpha and :omega
scope :upcoming, lambda { |field|
where(":field BETWEEN to_date(:alpha,'yyyy/mm/dd') AND to_date(:omega,'yyyy/mm/dd')",
field: field,
alpha: Time.now.advance(days: 4).strftime('%Y/%m/%d'),
omega: Time.now.advance(days: 8).strftime('%Y/%m/%d'),
)
}
I have no way of testing this so I might be off in the weeds here.
Validating user input as per Jordan is always a good idea.

Related

Rails SQL Injection: How vulnerable is this code?

I'm trying to understand SQL Injection. It seems like people can get pretty creative. Which gets me wondering about my search-based rails webapp I'm making.
Suppose I just fed user-entered information directly into the "where" statement of my SQL query. How much damage could be done to my database by allowing this?
def self.search(search)
if search
includes(:hobbies, :addresses).where(search)
else
self.all
end
So basically, whatever the user types into the search bar on the home page gets fed straight into that 'where' statement.
An example of a valid 'search' would be:
"hobby LIKE ? OR (gender LIKE ? AND hobby LIKE ?)", "golf", "male", "polo"
Does the fact that it's limited to the context of a 'where' statement provide any sort of defense? Could they still somehow perform delete or create operations?
EDIT:
When I look at this tutorial, I don't see a straightforward way to perform a deletion or creation action out of the where clause. If my database contains no information that I'm not willing to display from a valid search result, and there's no such thing as user accounts or admin privileges, what's really the danger here?
I took this from another post here: Best way to go about sanitizing user input in rails
TL;DR
Regarding user input and queries: Make sure to always use the active record query methods (such as .where), and avoid passing parameters using string interpolation; pass them as hash parameter values, or as parameterized statements.
Regarding rendering potentially unsafe user-generated html / javascript content: As of Rails 3, html/javascript text is automatically properly escaped so that it appears as plain text on the page, rather than interpreted as html/javascript, so you don't need to explicitly sanitize (or use <%= h(potentially_unsafe_user_generated_content)%>
If I understand you correctly, you don't need to worry about sanitizing data in this manner, as long as you use the active record query methods correctly. For example:
Lets say our parameter map looks like this, as a result of a malicious user inputting the following string into the user_name field:
:user_name => "(select user_name from users limit 1)"
The bad way (don't do this):
Users.where("user_name = #{params[:id}") # string interpolation is bad here
The resulting query would look like:
SELECT users.* FROM users WHERE (user_name = (select user_name from users limit 1))
Direct string interpolation in this manner will place the literal contents of the parameter value with key :user_name into the query without sanitization. As you probably know, the malicious user's input is treated as plain 'ol SQL, and the danger is pretty clear.
The good way (Do this):
Users.where(id: params[:id]) # hash parameters
OR
Users.where("id = ?", params[:id]) # parameterized statement
The resulting query would look like:
SELECT users.* FROM users WHERE user_name = '(select user_name from users limit 1)'
So as you can see, Rails in fact sanitizes it for you, so long as you pass the parameter in as a hash, or method parameter (depending on which query method you're using).
The case for sanitization of data on creating new model records doesn't really apply, as the new or create methods are expecting a hash of values. Even if you attempt to inject unsafe SQL code into the hash, the values of the hash are treated as plain strings, for example:
User.create(:user_name=>"bobby tables); drop table users;")
Results in the query:
INSERT INTO users (user_name) VALUES ('bobby tables); drop table users;')
So, same situation as above.
I hope that helps. Let me know if I've missed or misunderstood anything.
Edit Regarding escaping html and javascript, the short version is that ERB "escapes" your string content for you so that it is treated as plain text. You can have it treated like html if you really want, by doing your_string_content.html_safe.
However, simply doing something like <%= your_string_content %> is perfectly safe. The content is treated as a string on the page. In fact, if you examine the DOM using Chrome Developer Tools or Firebug, you should in fact see quotes around that string.

How to save nil into serialized attribute in Rails 4.2

I am upgrading an app to Rails 4.2 and am running into an issue where nil values in a field that is serialized as an Array are getting interpreted as an empty array. Is there a way to get Rails 4.2 to differentiate between nil and an empty array for a serialized-as-Array attribute?
Top level problem demonstration:
#[old_app]
> Rails.version
=> "3.0.3"
> a = AsrProperty.new; a.save; a.keeps
=> nil
#[new_app]
> Rails.version
=> "4.2.3"
> a = AsrProperty.new; a.save; a.keeps
=> []
But it is important for my code to distinguish between nil and [], so this is a problem.
The model:
class AsrProperty < ActiveRecord::Base
serialize :keeps, Array
#[...]
end
I think the issue lies with Rails deciding to take a shortcut for attributes that are serialized as a specific type (e.g. Array) by storing the empty instance of that type as nil in the database. This can be seen by looking at the SQL statement executed in each app:
[old_app]: INSERT INTO asr_properties (lock_version, keeps)
VALUES (0, NULL)
Note that the above log line has been edited for clarity; there are other serialized attributes that were being written due to old Rails' behavior.
[new_app]: INSERT INTO asr_properties (lock_version)
VALUES (0)
There is a workaround: by removing the "Array" declaration on the serialization, Rails is forced to save [] and {} differently:
class AsrProperty < ActiveRecord::Base
serialize :keeps #NOT ARRAY
#[...]
end
Changing the statement generated on saving [] to be:
INSERT INTO asr_properties (keeps, lock_version) VALUES ('---[]\n', 0)
Allowing:
> a = AsrProperty.new; a.save; a.keeps
=> nil
I'll use this workaround for now, but:
(1) I feel like declaring a type might allow more efficiency, and also prevents bugs by explicitly prohibiting the wrong data type being stored
(2) I'd really like to figure out the "right" way to do it, if Rails does allow it.
So: can Rails 4.2 be told to store [] as its own thing in a serialized-as-Array attribute?
What's going on?
What you're experiencing is due to how Rails 4 treats the 2nd argument to the serialize call. It changes its behavior based on the three different values the argument can have (more on this in the solution). The last branch here is the one we're interested in as when you pass the Array class, it gets passed to the ActiveRecord::Coders::YAMLColumn instance that is created. The load method receives the YAML from the database and attempts to turn it back into a Ruby object here. If the coder was not given the default class of Object and the yaml argument is nil in the case of a null column, it will return a new instance of the class, hence the empty array.
Solution
There doesn't appear to be a simple Rails-y way to say, "hey, if this is null in the database, give me nil." However looking at the second branch here we see that we can pass any object that implements the load and dump methods or what I call the basic coder protocol.
Example code
One of the members of my team built this simple class to handle just this case.
class NullableSerializer < ActiveRecord::Coders::YAMLColumn
def load(yaml)
return nil if yaml.nil?
super
end
end
This class inherits from the same YAMLColumn class provided by ActiveRecord so it already handles the load and dump methods. We do not need any modifications to dump but we want to slightly handle loading differently. We simply tell it to return nil when the database column is empty and otherwise call super to work as if we made no other modification.
Usage
To use it, it simply needs to be instantiated with your intended serialization class and passed to the Rails serialize method as in the following, using your naming from above:
class AsrProperty < ActiveRecord::Base
serialize :keeps, NullableSerializer.new(Array)
# …
end
The "right" way
Getting things done and getting your code shipped is paramount and I hope this helps you. After all, if the code isn't being used and doing good, who cares how ideal it is?
I would argue that Rails' approach is the right way in this case especially when you take Ruby's philosophy of The Principle of Least Surprise into account. When an attribute can possibly be an array, it should always return that type, even if empty, to avoid having to constantly special case nil. I would argue the same for any database column that you can put a reasonable default on (i.e. t.integer :anything_besides_a_foreign_key, default: 0). I've always been grateful to past-Aaron for remembering this most of the time whenever I get an unexpected NoMethodError: undefined method 'whatever' for nil:NilClass. Almost always my special case for this nil is to supply a sensible default.
This varies greatly on you, your team, your app, and your application and it's needs so it's never hard and fast. It's just something I've found helps me out immensely when I'm working on something and wondering if amount could default to 0 or if there's some reason buried in the code or in the minds of your teammates why it needs to be able to be nil.

Is ActiveRecord's “order” method passed with hash vulnerable to SQL injection?

Adding to the same question from (HERE), I am planning to use hash instead of string as parameter. Say,
User.order(params[:column].to_sym => params[:direction].to_sym)
Where params[:column] and params[:direction] are passed from the page for sorting the table (Reference). I even added .to_sym to both parameters just so that it will be forced into a symbol instead of string just to be safe (although I am not sure if this is even necessary)
Now, I would just like to know if this approach is safe.
P.S. tried ransack gem, however I couldn't do nested queries. So I wrote my own customizable one.
I think this is at least still open for a Denail of Service attack.
http://brakemanscanner.org/docs/warning_types/denial_of_service/index.html
The reference is from a nice gem called brakeman which finds vunerable things in a rails application.
In general I would advise you to use #dmcnally's approach from the other issue you posted.
Here an example of what I did in my own projects:
SORT = { newest: { created_at: :desc },
cheapest: { price: :asc },
most_expensive: { price: :desc }
}.stringify_keys
And then use SORT[param[:sort]] to get the sort order. You can also do this by using two seperate hashes for direction and column like you supposed. If you use brakeman you will be able to have a little but of safety since it finds most things like that.
Symbols don't protect you from SQL injection, query parametrization protects you from SQL injection - and this only on the value side, not on the column name side. The thing to take from the other article is "not safe to use interpolated strings in column name when calling .order", not "not safe to use strings when calling .order",
your example defines ordering using a hash - that hash gets translated into a parametrized SQL query in AR, so it is safe as long as you sanitize the column name. One liberal way to do this is to:
raise "Unknown column name #{params[:column]}" unless YourModel.column_names.include?(params[:column])
PS What .to_sym does in your example is that it enables a third party to define a new symbol on the ruby vm. Symbols are never garbage collected so the attacker can send many different values so that your ruby processes hog the system memory - thus opening you to a ddos attack. The cast in the end does nothing because if you look here you'll notice your value gets cast into string anyway :)

Variable in select in active record in ruby on rails

I have a simple active record query.
Product.active.select('stats_date, clicks_through')
I want to make clicks_through as a variable that will contain name of the attribute.
I tried
.select('stats_date, #{type_of_data}')
But this is not working. Any suggestions?
If type_of_data is a variable that holds a string or symbol that is a column in your database, the following should work (assuming that Product.active is a scope that returns an ActiveRecord::Relation).
Just change it to:
.select("stats_date, "#{type_of_data}")
(note the double quotes, which are required to use string interpolation in Ruby).
It's been almost 10 years, but there are some public methods available for this;
User.select(
User.sanitize_sql_array([':column', column: 'id'])
)
# "SELECT 'id' FROM \"users\"
It's basically that, you use the methods that ActiveRecord provides to sanitize the arguments for select.
Whether your query is prone to SQLi or not will depend entirely on the implementation of the sanitize_ methods.

Rails: Convert string to variable (to store a value)

I have a parameter hash that contains different variable and name pairs such as:
param_hash = {"system_used"=>"metric", "person_height_feet"=>"5"}
I also have an object CalculationValidator that is not an ActiveRecord but a ActiveModel::Validations. The Object validates different types of input from forms. Thus it does not have a specific set of variables.
I want to create an Object to validate it like this:
validator = CalculationValidator.new()
validator.system_used = "metric"
validator.person_height_feet = 5
validator.valid?
my problem right now is that I really would not prefer to code each CalculationValidator manually but rather use the information in the Hash. The information is all there so what I would like to do is something like this, where MAKE_INTO_VARIABLE() is the functionality I am looking for.
validator = CalculationValidator.new()
param_hash.each do |param_pair|
["validator.", param_pair[0]].join.MAKE_INTO_VARIABLE() = param_pair[1]
# thus creating
# "validator.system_used".MAKE_INTO_VARIABLE() = "metric"
# while wanting: validator.system_used = "metric"
# ...and in the next loop
# "validator.person_height_feet".MAKE_INTO_VARIABLE() = 5
# while wanting: validator.person_height_feet = 5
end
validator.valid?
Problem:
Basically my problem is, how do I make the string "validator.person_height" into the variable validator.person_height that I can use to store the number 5?
Additionally, it is very important that the values of param_pair[1] are stored as their real formats (integer, string etc) since they will be validated.
I have tried .send() and instance_variable_set but I am not sure if they will do the trick.
Something like this might work for you:
param_hash.each do |param, val|
validator.instance_eval("def #{param}; ##{param} end")
validator.instance_variable_set("##{param}", val)
end
However, you might notice there's no casting or anything here. You'd need to communicate what type of value each is somehow, as it can't be assumed that "5" is supposed to be an integer, for example.
And of course I probably don't have to mention, eval'ing input that comes in from a form isn't exactly the safest thing in the world, so you'd have to think about how you want to handle this.
Have you looked at eval. As long as you can trust the inputs it should be ok to use.

Resources