~ubuntu-branches/ubuntu/vivid/ruby-sequel/vivid

« back to all changes in this revision

Viewing changes to doc/security.rdoc

  • Committer: Package Import Robot
  • Author(s): Dmitry Borodaenko, Dmitry Borodaenko, Cédric Boutillier
  • Date: 2013-08-10 18:38:17 UTC
  • mfrom: (1.1.8)
  • Revision ID: package-import@ubuntu.com-20130810183817-iqanz804j32i5myi
Tags: 4.1.1-1
[ Dmitry Borodaenko ]
* New upstream release.
* Standards-Version upgraded to 3.9.4 (no changes).
* Added Build-Depend on ruby-sqlite3.

[ Cédric Boutillier ]
* debian/control: remove obsolete DM-Upload-Allowed flag.
* use canonical URI in Vcs-* fields.
* debian/copyright: use DEP5 copyright-format/1.0 official URL for Format
  field.
* Update debian/watch. Thanks Bart Martens.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
= Security Considerations with Sequel
 
2
 
 
3
When using Sequel, there are some security areas you should be aware of:
 
4
 
 
5
* Code Execution
 
6
* SQL Injection
 
7
* Denial of Service
 
8
* Mass Assignment
 
9
* General Parameter Handling
 
10
 
 
11
== Code Execution
 
12
 
 
13
The most serious security vulnerability you can have in any library is
 
14
a code execution vulnerability.  Sequel should not be vulnerable to this,
 
15
as it never calls eval on a string that is derived from user input.
 
16
However, some Sequel methods used for creating methods via metaprogramming
 
17
could conceivably be abused to do so:
 
18
 
 
19
* Sequel::Schema::CreateTableGenerator.add_type_method
 
20
* Sequel::Dataset.def_mutation_method
 
21
* Sequel::Model::Plugins.def_dataset_methods
 
22
* Sequel.def_adapter_method (private)
 
23
* Sequel::SQL::Expression.to_s_method (private)
 
24
* Sequel::Plugins::HookClassMethods::ClassMethods#add_hook_type
 
25
 
 
26
As long as you don't call those with user input, you should not be
 
27
vulnerable to code execution.
 
28
 
 
29
== SQL Injection
 
30
 
 
31
The primary security concern in SQL database libraries is SQL injection.
 
32
Because Sequel promotes using ruby objects for SQL concepts instead
 
33
of raw SQL, it is less likely to be vulnerable to SQL injection.
 
34
However, because Sequel still makes it easy to use raw SQL, misuse of the
 
35
library can result in SQL injection in your application.
 
36
 
 
37
There are basically two kinds of possible SQL injections in Sequel:
 
38
 
 
39
* SQL code injections
 
40
* SQL identifier injections
 
41
 
 
42
=== SQL Code Injections
 
43
 
 
44
==== Full SQL Strings
 
45
 
 
46
Some Sequel methods are designed to execute raw SQL, including:
 
47
 
 
48
* Sequel::Database#execute
 
49
* Sequel::Database#run
 
50
* Sequel::Database#<<
 
51
* Sequel::Database#[]
 
52
* Sequel::Database#fetch
 
53
* Sequel::Dataset#with_sql
 
54
 
 
55
Here are some examples of use:
 
56
 
 
57
  DB.run 'SQL'
 
58
  DB << 'SQL'
 
59
  DB.execute 'SQL'
 
60
  DB['SQL'].all
 
61
  DB.fetch('SQL').all
 
62
  DB.dataset.with_sql('SQL').all
 
63
 
 
64
If you pass a string to these methods that is derived from user input, you open
 
65
yourself up to SQL injection.  The Sequel::Database#run, Sequel::Database#<<, and
 
66
Sequel::Database#execute methods are not designed to work at all with user input.
 
67
If you must use them with user input, you should escape the user input manually
 
68
via Sequel::Database#literal. Example:
 
69
 
 
70
  DB.run "SOME SQL #{DB.literal(params[:user].to_s)}"
 
71
 
 
72
With Sequel::Database#[], Sequel::Database#fetch and Sequel::Dataset#with_sql, you should use placeholders,
 
73
in which case Sequel automatically literalizes the input:
 
74
 
 
75
  DB['SELECT * FROM foo WHERE bar = ?', params[:user].to_s]
 
76
 
 
77
==== Manually Created Literal Strings
 
78
 
 
79
Sequel generally treats ruby strings as SQL strings (escaping them correctly), and
 
80
not as raw SQL.  However, you can convert a ruby string to a literal string, and
 
81
Sequel will then treat it as raw SQL.  This is typically done through String#lit
 
82
if the {core_extensions}[link:files/doc/core_extensions_rdoc.html] are in use,
 
83
or Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit] if they are not in use.
 
84
 
 
85
  'a'.lit
 
86
  Sequel.lit('a')
 
87
 
 
88
Using String#lit or Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit] to turn a ruby string into a literal string results
 
89
in SQL injection if the string is derived from user input.  With both of these
 
90
methods, the strings can contain placeholders, which you can use to safely include
 
91
user input inside a literal string:
 
92
 
 
93
  'a = ?'.lit(params[:user_id].to_s)
 
94
  Sequel.lit('a = ?', params[:user_id].to_s)
 
95
 
 
96
Even though they have similar names, note that Sequel::Database#literal operates very differently from
 
97
String#lit or Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit].
 
98
Sequel::Database#literal is for taking any supported object,
 
99
and getting an SQL representation of that object, while
 
100
String#lit or Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit] are for treating
 
101
a ruby string as raw SQL.  For example:
 
102
 
 
103
  DB.literal(Date.today) # "'2013-03-22'"
 
104
  DB.literal('a') # "'a'"
 
105
  DB.literal(Sequel.lit('a')) # "a"
 
106
  DB.literal(:a => 'a') # "(\"a\" = 'a')"
 
107
  DB.literal(:a => Sequel.lit('a')) # "(\"a\" = a)"
 
108
 
 
109
==== SQL Filter Fragments
 
110
 
 
111
The most common way to use raw SQL with Sequel is in filters:
 
112
 
 
113
  DB[:table].where("name > 'M'")
 
114
 
 
115
If a filter method is passed a string as the first argument, it treats the rest of
 
116
the arguments (if any) as placeholders to the string.  So you should never do:
 
117
 
 
118
  DB[:table].where("name > #{params[:id].to_s}") # SQL Injection!
 
119
 
 
120
Instead, you should use a placeholder:
 
121
 
 
122
  DB[:table].where("name > ?", params[:id].to_s) # Safe
 
123
 
 
124
Note that for that type of query, Sequel generally encourages the following form:
 
125
 
 
126
  DB[:table].where{|o| o.name > params[:id].to_s} # Safe
 
127
 
 
128
Sequel's DSL supports a wide variety of SQL concepts, so it's possible to
 
129
code most applications without every using raw SQL.
 
130
 
 
131
A large number of dataset methods ultimately pass down their arguments to a filter
 
132
method, even some you may not expect, so you should be careful.  At least the
 
133
following methods pass their arguments to a filter method:
 
134
 
 
135
* Sequel::Dataset#where
 
136
* Sequel::Dataset#having
 
137
* Sequel::Dataset#filter
 
138
* Sequel::Dataset#exclude
 
139
* Sequel::Dataset#exclude_where
 
140
* Sequel::Dataset#exclude_having
 
141
* Sequel::Dataset#and
 
142
* Sequel::Dataset#or
 
143
* Sequel::Dataset#first
 
144
* Sequel::Dataset#last
 
145
* Sequel::Dataset#[]
 
146
* Sequel::Dataset#[]=
 
147
 
 
148
The Model.find[rdoc-ref:Sequel::Model::ClassMethods#find] and Model.find_or_create[rdoc-ref:Sequel::Model::ClassMethods#find_or_create]
 
149
class methods also call down to the filter methods.
 
150
 
 
151
==== SQL Fragment passed to Dataset#update
 
152
 
 
153
Similar to the filter methods, Sequel::Dataset#update also treats a
 
154
string argument as raw SQL:
 
155
 
 
156
  DB[:table].update("column = 1")
 
157
 
 
158
So you should not do:
 
159
 
 
160
  DB[:table].update("column = #{params[:value].to_s}") # SQL Injection!
 
161
 
 
162
Instead, you should do:
 
163
 
 
164
  DB[:table].update(:column => params[:value].to_s) # Safe
 
165
 
 
166
==== SQL Fragment passed to Dataset#lock_style
 
167
 
 
168
The Sequel::Dataset#lock_style method also treats an input string 
 
169
as SQL code. This method should not be called with user input.
 
170
 
 
171
=== SQL Identifier Injections
 
172
 
 
173
Usually, Sequel treats ruby symbols as SQL identifiers, and ruby
 
174
strings as SQL strings.  However, there are some parts of Sequel
 
175
that treat ruby strings as SQL identifiers if an SQL string would
 
176
not make sense in the same context.
 
177
 
 
178
For example, Sequel::Database#from and Sequel::Dataset#from will treat a string as
 
179
a table name:
 
180
 
 
181
  DB.from('t') # SELECT * FROM "t"
 
182
 
 
183
Another place where Sequel treats ruby strings as identifiers are
 
184
the Sequel::Dataset#insert and Sequel::Dataset#update methods:
 
185
 
 
186
  DB[:t].update('b'=>1) # UPDATE "t" SET "b" = 1
 
187
  DB[:t].insert('b'=>1) # INSERT INTO "t" ("b") VALUES (1)
 
188
 
 
189
Note how the identifier is still quoted in these cases.  Sequel quotes identifiers by default
 
190
on most databases.  However, it does not quote identifiers by default on DB2 and Informix.
 
191
On those databases using an identifier derived from user input can lead to SQL injection.
 
192
Similarly, if you turn off identifier quoting manually on other databases, you open yourself
 
193
up to SQL injection if you use identifiers derived from user input.
 
194
 
 
195
When Sequel quotes identifiers, using an identifier derived from user input does not lead to
 
196
SQL injection, since the identifiers are also escaped when quoting.
 
197
Exceptions to this are Oracle (can't escape <tt>"</tt>) and Microsoft Access
 
198
(can't escape <tt>]</tt>).
 
199
 
 
200
In general, even if doesn't lead to SQL Injection, you should avoid using identifiers
 
201
derived from user input unless absolutely necessary.
 
202
 
 
203
Sequel also allows you to create identifiers using
 
204
Sequel.identifier[rdoc-ref:Sequel::SQL::Builders#identifier] for plain identifiers,
 
205
Sequel.qualify[rdoc-ref:Sequel::SQL::Builders#qualify] for qualified identifiers, and
 
206
Sequel.as[rdoc-ref:Sequel::SQL::Builders#as] for aliased expressions.  So if you
 
207
pass any of those values derived from user input, you are dealing with the same scenario.
 
208
 
 
209
Note that the issues with SQL identifiers do not just apply to places where
 
210
strings are used as identifiers, they also apply to all places where Sequel
 
211
uses symbols as identifiers.  However, if you are creating symbols from user input,
 
212
you at least have a denial of service vulnerability, and possibly a more serious
 
213
vulnerability.
 
214
 
 
215
== Denial of Service
 
216
 
 
217
Sequel converts some strings to symbols.  Because symbols in ruby are not
 
218
garbage collected, if the strings that are converted to symbols are
 
219
derived from user input, you have a denial of service vulnerability due to
 
220
memory exhaustion.
 
221
 
 
222
The strings that Sequel converts to symbols are generally not derived
 
223
from user input, so Sequel in general is not vulnerable to this.  However,
 
224
users should be aware of the cases in which Sequel creates symbols, so
 
225
they do not introduce a vulnerability into their application.
 
226
 
 
227
=== Column Names/Aliases
 
228
 
 
229
Sequel returns SQL result sets as an array of hashes with symbol keys.  The
 
230
keys are derived from the name that the database server gives the column. These
 
231
names are generally static.  For example:
 
232
 
 
233
  SELECT column FROM table
 
234
 
 
235
The database will generally use "column" as the name in the result set.
 
236
 
 
237
If you use an alias:
 
238
 
 
239
  SELECT column AS alias FROM table
 
240
 
 
241
The database will generally use "alias" as the name in the result set. So
 
242
if you allow the user to control the alias name:
 
243
 
 
244
  DB[:table].select(:column.as(params[:alias]))
 
245
 
 
246
Then you have a denial of service vulnerability.  In general, such a vulnerability
 
247
is unlikely, because you are probably indexing into the returned hash(es) by name,
 
248
and if an alias was used and you didn't expect it, your application wouldn't work.
 
249
 
 
250
=== Database Connection Options
 
251
 
 
252
All database connection options are converted to symbols.  For a
 
253
connection URL, the keys are generally fixed, but the scheme is turned
 
254
into a symbol and the query option keys are used as connection option
 
255
keys, so they are converted to symbols as well.  For example:
 
256
 
 
257
  postgres://host/database?option1=foo&option2=bar
 
258
 
 
259
Will result in :postgres, :option1, and :option2 symbols being created.
 
260
 
 
261
Certain option values are also converted to symbols.  In the general case,
 
262
the sql_log_level option value is, but some adapters treat additional
 
263
options similarly.
 
264
 
 
265
This is not generally a risk unless you are allowing the user to control
 
266
the connection URLs or are connecting to arbitrary databases at runtime.
 
267
 
 
268
== Mass Assignment
 
269
 
 
270
Mass assignment is the practice of passing a hash of columns and values
 
271
to a single method, and having multiple column values for a given object set
 
272
based on the content of the hash.
 
273
The security issue here is that mass assignment may allow the user to
 
274
set columns that you didn't intend to allow.
 
275
 
 
276
The Model#set[rdoc-ref:Sequel::Model::InstanceMethods#set] and Model#update[rdoc-ref:Sequel::Model::InstanceMethods#update] methods do mass
 
277
assignment.  The default configuration of Sequel::Model allows all model
 
278
columns except for the primary key column(s) to be set via mass assignment.
 
279
 
 
280
Example:
 
281
 
 
282
  album = Album.new
 
283
  album.set(params[:album]) # Mass Assignment
 
284
 
 
285
Both Model.new[rdoc-ref:Sequel::Model::InstanceMethods::new] and Model.create[rdoc-ref:Sequel::Model::ClassMethods#create]
 
286
call Model#set[rdoc-ref:Sequel::Model::InstanceMethods#set] internally, so
 
287
they also allow mass assignment:
 
288
 
 
289
  Album.new(params[:album]) # Mass Assignment
 
290
  Album.create(params[:album]) # Mass Assignment
 
291
 
 
292
Instead of these methods, it is encouraged to either use the
 
293
Model#set_only[rdoc-ref:Sequel::Model::InstanceMethods#set_only],
 
294
Model#update_only[rdoc-ref:Sequel::Model::InstanceMethods#update_only],
 
295
Model#set_fields[rdoc-ref:Sequel::Model::InstanceMethods#set_fields], or
 
296
Model#update_fields[rdoc-ref:Sequel::Model::InstanceMethods#update_fields]
 
297
methods, which allow you to specify which fields
 
298
to allow on a per-call basis.  This pretty much eliminates the chance that the
 
299
user will be able to set a column you did not intend to allow:
 
300
 
 
301
  album.set_only(params[:album], [:name, :copies_sold])
 
302
  album.set_fields(params[:album], [:name, :copies_sold])
 
303
 
 
304
You can override the columns to allow by default during mass assignment via
 
305
the Model.set_allowed_columns[rdoc-ref:Sequel::Model::ClassMethods#set_allowed_columns] class method.  This is a good
 
306
practice, though being explicit on a per-call basis is still recommended:
 
307
 
 
308
  Album.set_allowed_columns(:name, :copies_sold)
 
309
  Album.create(params[:album]) # Only name and copies_sold set
 
310
 
 
311
For more details on the mass assignment methods, see the {Mass Assignment Guide}[link:files/doc/mass_assignment_rdoc.html].
 
312
 
 
313
== General Parameter Handling
 
314
 
 
315
This issue isn't necessarily specific to Sequel, but it is a good general practice.
 
316
If you are using values derived from user input, it is best to be explicit about
 
317
their type.  For example:
 
318
 
 
319
  Album.where(:id=>params[:id])
 
320
 
 
321
is probably a bad idea.  Assuming you are using a web framework, params\[:id\] could
 
322
be a string, an array, a hash, or nil.
 
323
 
 
324
Assuming that +id+ is an integer field, you probably want to do:
 
325
 
 
326
  Album.where(:id=>params[:id].to_i)
 
327
 
 
328
If you are looking something up by name, you should try to enforce the value to be
 
329
a string:
 
330
 
 
331
  Album.where(:name=>params[:name].to_s)
 
332
 
 
333
If you are trying to use an IN clause with a list of id values based on input provided
 
334
on a web form:
 
335
 
 
336
  Album.where(:id=>params[:ids].to_a.map{|i| i.to_i})
 
337
 
 
338
Basically, be as explicit as possible. While there aren't any known security issues
 
339
in Sequel when you do:
 
340
 
 
341
  Album.where(:id=>params[:id])
 
342
 
 
343
It allows the attacker to choose to do any of the following queries:
 
344
 
 
345
  id IS NULL # nil
 
346
  id = '1' # '1'
 
347
  id IN ('1', '2', '3') # ['1', '2', '3']
 
348
  id = ('a' = 'b') # {'a'=>'b'}
 
349
  id = ('a' IN ('a', 'b') AND 'c' = '') # {'a'=>['a', 'b'], 'c'=>''}
 
350
 
 
351
While none of those allow for SQL injection, it's possible that they
 
352
might have an issue in your application.  For example, a long array
 
353
or deeply nested hash might cause the database to have to do a lot of
 
354
work that could be avoided.
 
355
 
 
356
In general, it's best to let the attacker control as little as possible,
 
357
and explicitly specifying types helps a great deal there.