I have a trigger which passes some parameters to a procedure and invoke it.
CREATE TRIGGER trig_ins INSERT ON mytable
REFERENCING NEW AS new
FOR EACH ROW(EXECUTE PROCEDURE sp_ins("mytable", new.id, 'I'));
Is there any way I can pass concatenated char as the second parameter to the above procedure call?
I need this because I may need to allow these parameters:
"id = " . new.id
"field1 =" . new.field1 . "and field2 = " . new.field2
I get the impression this is to produce some sort of shared audit or logging table. You'll obviously need to define the TRIGGER on a per-table basis, so with each declaration, you nominate the relevant fields, e.g.
CREATE TRIGGER trig_ins INSERT ON mytable
REFERENCING NEW AS new
FOR EACH ROW(EXECUTE PROCEDURE sp_ins("mytable",
"field1 = " || new.field1 || " and field2 = " || new.field2, 'I'));
But it looks and feels a bit clumsy. I can't help thinking this is an XY problem of some sort.
UPDATE
(Following comment 'need not be where_clause')
Well, the obvious answer is to make sure every table has a simple, single surrogate key column, that can be treated like a ROWID.
That's more easily said than done when you have an existing model, of course. If that's not possible, then what you want is a representation of your compound key in a manner that can be parsed later if need be. Exactly how you do that depends on how you intend to parse it: programmatically or through preconstructed SQL fragment as you first proposed. The former is more controllable, but won't produce 'injectable' SQL fragments:
EXECUTE PROCEDURE sp_ins("mytable",
"<field1>" || NVL(new.field1,"") || "</field1>" ||
"<field2>" || NVL(new.field2,"") || "</field2>",
"I");
...is one way of doing it. You can do the SQL fragment approach, it's just the construction is very messy, as shown above. For example, if field1 is a string, the SQL is already broken, and you need to do something like this:
EXECUTE PROCEDURE sp_ins("mytable",
"field1 " || NVL('= "'||new.field1||'"','IS NULL') || ' AND ' ||
"field2 " || NVL('= "'||new.field2||'"','IS NULL'),
"I")
... and you can be sure that sooner or later you'll run into an Irish problem where a name like O'Malley or Sylvester "Sly" Stallone breaks those embedded quotes. There's no elegant solution because it's an inelegant thing you're trying to do.
Informix provides out-of-the-box audit features, and ways and means of exploring the logical logs. I can't help thinking you'd be better off exploring those.
Related
So inside a Where Active Record(AR) query you can do:
game_stickers.where('stickers.name != ?', 'Ban')
But how can you test matches against multiple strings with an OR operator without doing something like:
game_stickers.where('stickers.name != ? OR stickers.name != ?', 'Ban', 'Closed')
or without reverting to something like [see note below]:
game_stickers.where.not(stickers.name: ['Ban','Closed','Block'])
NOTE:
The reason I do not want to go with the last alternative is because I'm using some joins and references in my queries that (as far as I can see) do not play nicely with this option. The context code goes something like:
game_stickers_and_stickers = game_stickers.includes(:sticker)
game_stickers_and_stickers.where('stickers.name = ?', 'Ban').references(:stickers).where(placement_side: side)
Maybe a you can advise on the optimal way to do this query.
Note: it seems to me you want an AND between those conditions, not an OR. Think about it. Anyway, try this one
game_stickers_and_stickers = game_stickers.includes(:sticker)
game_stickers_and_stickers.where.not(stickers: {name: ['Ban','Closed','Block']}).where(placement_side: side)
that condition fragment should be converted to the SQL
WHERE stickers.name NOT IN ('Ban', 'Closed', 'Block')
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.
Am used to working with PHP and Prepared statement, now when i was looking at the following piece of code from rails ( since i a new to rails and Not sure about the syntax and stuff ) , i was wondering if the code is prone to SQLI injection
Code snippet (controller ) , param q is the value from a search box :
def index
query = %w(% %).join params[:q].to_s.gsub('%', '\\%').gsub('_', '\\_')
#posts = Post.where("name LIKE ? OR body LIKE ?", query, query).order(params[:order])
end
Thanks
What you have is intended to be safe. If it is not, then it's a bug in Rails.
.where accepts conditions in several formats. One is a raw string. If you build that string yourself, all bets are off and you are vulnerable.
As some recent documentation says:
Note that building your own string from user input may expose your
application to injection attacks if not done properly. As an
alternative, it is recommended to use one of the following methods.
In other words, ALL of the "following" (every other supported way) ways of doing things, are OK.
So if you are doing .where with anything other than string parameter, you should be fine.
As long as you don't interpolate within your where clause it should be safe. There are some good examples of SQL injection code here
This relates to the lucene-based search engine, Ferret.
https://github.com/dbalmain/ferret
Let's say i have a model with two fields, myfield1 and myfield2. I want to get records that have myfield equal to "foo", or that have null (or an empty string) for myfield but have myfield2 set to "foo".
I DON'T want to get records that have, for example, myfield = "bar" and myfield2 = "foo". So, it's not as simple as just saying "myfield:foo || myfield2: foo" - i only want to look at myfield2 if myfield is empty.
The sql equivalent would be where (myfield = 'foo') or ((myfield is null or myfield = '') and myfield2 = 'foo'). What would the ferret search string equivalent of this be?
The following doesn't work, but it's an example of the sort of thing I'm after:
"myfield:foo || (myfield:<blank> && myfield2:foo)"
thanks, Max
BTW in case it's relevant i'm using acts_as_ferret in ruby on rails, but i think my question really just relates to a ferret search string. I'm using the ferret gem, v=0.11.6
EDIT: Slightly dirty-feeling solution below, would still like to know if it's possible just with the query string like above.
OK, i got around this by adding a new method, "myfield_blank":
def myfield_blank
myfield_blank?.to_s
end
then adding myfield_blank => {}, to my acts_as_ferret index definition. So now i can say
"myfield:foo || (myfield_blank:true && myfield2:foo)"
This works but like I say i'd still like to know if I can just do it in the query, without making new fields: this approach would be unacceptably hacky if i wanted to do it for lots of different fields. thanks
According to the source ?* should match any nonempty string, so you can try to do it this way:
'myfield:"foo" || (-myfield:"?*" && myfield2:"foo")'
And also I don't really see why 'myfield:"foo" || (myfield:"" && myfield2:"foo")' shouldn't work, but you probably already tried it...
I want to execute one update raw sql like below:
update table set f1=? where f2=? and f3=?
This SQL will be executed by ActiveRecord::Base.connection.execute, but I don't know how to pass the dynamic parameter values into the method.
Could someone give me any help on it?
It doesn't look like the Rails API exposes methods to do this generically. You could try accessing the underlying connection and using it's methods, e.g. for MySQL:
st = ActiveRecord::Base.connection.raw_connection.prepare("update table set f1=? where f2=? and f3=?")
st.execute(f1, f2, f3)
st.close
I'm not sure if there are other ramifications to doing this (connections left open, etc). I would trace the Rails code for a normal update to see what it's doing aside from the actual query.
Using prepared queries can save you a small amount of time in the database, but unless you're doing this a million times in a row, you'd probably be better off just building the update with normal Ruby substitution, e.g.
ActiveRecord::Base.connection.execute("update table set f1=#{ActiveRecord::Base.sanitize(f1)}")
or using ActiveRecord like the commenters said.
ActiveRecord::Base.connection has a quote method that takes a string value (and optionally the column object). So you can say this:
ActiveRecord::Base.connection.execute(<<-EOQ)
UPDATE foo
SET bar = #{ActiveRecord::Base.connection.quote(baz)}
EOQ
Note if you're in a Rails migration or an ActiveRecord object you can shorten that to:
connection.execute(<<-EOQ)
UPDATE foo
SET bar = #{connection.quote(baz)}
EOQ
UPDATE: As #kolen points out, you should use exec_update instead. This will handle the quoting for you and also avoid leaking memory. The signature works a bit differently though:
connection.exec_update(<<-EOQ, "SQL", [[nil, baz]])
UPDATE foo
SET bar = $1
EOQ
Here the last param is a array of tuples representing bind parameters. In each tuple, the first entry is the column type and the second is the value. You can give nil for the column type and Rails will usually do the right thing though.
There are also exec_query, exec_insert, and exec_delete, depending on what you need.
None of the other answers showed me how to use named parameters, so I ended up combining exec_update with sanitize_sql:
User.connection.exec_update(
User.sanitize_sql(
[
"update users set name = :name where id = :id and name <> :name",
{
id: 123,
name: 'My Name'
}
]
)
)
This works for me on Rails 5, and it executes this SQL:
update users set name = 'My Name' where id = 123 and name <> 'My Name'
You need to use an existing Rails model instead of User if you don't have that.
I wanted to use named parameters to avoid issues with the ordering when I use ? or $1/$2,etc. Positional ordering is kind of frustrating when I have more than a handful of parameters, but named parameters allow me to refactor the SQL command without having to update the parameters.
You should just use something like:
YourModel.update_all(
ActiveRecord::Base.send(:sanitize_sql_for_assignment, {:value => "'wow'"})
)
That would do the trick. Using the ActiveRecord::Base#send method to invoke the sanitize_sql_for_assignment makes the Ruby (at least the 1.8.7 version) skip the fact that the sanitize_sql_for_assignment is actually a protected method.
Sometime would be better use name of parent class instead name of table:
# Refers to the current class
self.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp)
For example "Person" base class, subclasses (and database tables) "Client" and "Seller"
Instead using:
Client.where(self.class.primary_key => id).update_all(created _at: timestamp)
Seller.where(self.class.primary_key => id).update_all(created _at: timestamp)
You can use object of base class by this way:
person.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp)
Here's a trick I recently worked out for executing raw sql with binds:
binds = SomeRecord.bind(a_string_field: value1, a_date_field: value2) +
SomeOtherRecord.bind(a_numeric_field: value3)
SomeRecord.connection.exec_query <<~SQL, nil, binds
SELECT *
FROM some_records
JOIN some_other_records ON some_other_records.record_id = some_records.id
WHERE some_records.a_string_field = $1
AND some_records.a_date_field < $2
AND some_other_records.a_numeric_field > $3
SQL
where ApplicationRecord defines this:
# Convenient way of building custom sql binds
def self.bind(column_values)
column_values.map do |column_name, value|
[column_for_attribute(column_name), value]
end
end
and that is similar to how AR binds its own queries.
I needed to use raw sql because I failed at getting composite_primary_keys to function with activerecord 2.3.8. So in order to access the sqlserver 2000 table with a composite primary key, raw sql was required.
sql = "update [db].[dbo].[#{Contacts.table_name}] " +
"set [COLUMN] = 0 " +
"where [CLIENT_ID] = '#{contact.CLIENT_ID}' and CONTACT_ID = '#{contact.CONTACT_ID}'"
st = ActiveRecord::Base.connection.raw_connection.prepare(sql)
st.execute
If a better solution is available, please share.
In Rails 3.1, you should use the query interface:
new(attributes)
create(attributes)
create!(attributes)
find(id_or_array)
destroy(id_or_array)
destroy_all
delete(id_or_array)
delete_all
update(ids, updates)
update_all(updates)
exists?
update and update_all are the operation you need.
See details here: http://m.onkey.org/active-record-query-interface