sequel 5.83.0 → 5.84.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sequel/adapters/shared/sqlite.rb +3 -1
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/misc.rb +8 -3
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/extensions/pg_json_ops.rb +328 -1
- data/lib/sequel/sql.rb +8 -5
- data/lib/sequel/version.rb +1 -1
- metadata +2 -236
- data/CHANGELOG +0 -1393
- data/README.rdoc +0 -936
- data/doc/advanced_associations.rdoc +0 -884
- data/doc/association_basics.rdoc +0 -1859
- data/doc/bin_sequel.rdoc +0 -146
- data/doc/cheat_sheet.rdoc +0 -255
- data/doc/code_order.rdoc +0 -104
- data/doc/core_extensions.rdoc +0 -405
- data/doc/dataset_basics.rdoc +0 -96
- data/doc/dataset_filtering.rdoc +0 -222
- data/doc/extensions.rdoc +0 -77
- data/doc/fork_safety.rdoc +0 -84
- data/doc/mass_assignment.rdoc +0 -98
- data/doc/migration.rdoc +0 -660
- data/doc/model_dataset_method_design.rdoc +0 -129
- data/doc/model_hooks.rdoc +0 -254
- data/doc/model_plugins.rdoc +0 -270
- data/doc/mssql_stored_procedures.rdoc +0 -43
- data/doc/object_model.rdoc +0 -563
- data/doc/opening_databases.rdoc +0 -439
- data/doc/postgresql.rdoc +0 -611
- data/doc/prepared_statements.rdoc +0 -144
- data/doc/querying.rdoc +0 -1070
- data/doc/reflection.rdoc +0 -120
- data/doc/release_notes/5.0.0.txt +0 -159
- data/doc/release_notes/5.1.0.txt +0 -31
- data/doc/release_notes/5.10.0.txt +0 -84
- data/doc/release_notes/5.11.0.txt +0 -83
- data/doc/release_notes/5.12.0.txt +0 -141
- data/doc/release_notes/5.13.0.txt +0 -27
- data/doc/release_notes/5.14.0.txt +0 -63
- data/doc/release_notes/5.15.0.txt +0 -39
- data/doc/release_notes/5.16.0.txt +0 -110
- data/doc/release_notes/5.17.0.txt +0 -31
- data/doc/release_notes/5.18.0.txt +0 -69
- data/doc/release_notes/5.19.0.txt +0 -28
- data/doc/release_notes/5.2.0.txt +0 -33
- data/doc/release_notes/5.20.0.txt +0 -89
- data/doc/release_notes/5.21.0.txt +0 -87
- data/doc/release_notes/5.22.0.txt +0 -48
- data/doc/release_notes/5.23.0.txt +0 -56
- data/doc/release_notes/5.24.0.txt +0 -56
- data/doc/release_notes/5.25.0.txt +0 -32
- data/doc/release_notes/5.26.0.txt +0 -35
- data/doc/release_notes/5.27.0.txt +0 -21
- data/doc/release_notes/5.28.0.txt +0 -16
- data/doc/release_notes/5.29.0.txt +0 -22
- data/doc/release_notes/5.3.0.txt +0 -121
- data/doc/release_notes/5.30.0.txt +0 -20
- data/doc/release_notes/5.31.0.txt +0 -148
- data/doc/release_notes/5.32.0.txt +0 -46
- data/doc/release_notes/5.33.0.txt +0 -24
- data/doc/release_notes/5.34.0.txt +0 -40
- data/doc/release_notes/5.35.0.txt +0 -56
- data/doc/release_notes/5.36.0.txt +0 -60
- data/doc/release_notes/5.37.0.txt +0 -30
- data/doc/release_notes/5.38.0.txt +0 -28
- data/doc/release_notes/5.39.0.txt +0 -19
- data/doc/release_notes/5.4.0.txt +0 -80
- data/doc/release_notes/5.40.0.txt +0 -40
- data/doc/release_notes/5.41.0.txt +0 -25
- data/doc/release_notes/5.42.0.txt +0 -136
- data/doc/release_notes/5.43.0.txt +0 -98
- data/doc/release_notes/5.44.0.txt +0 -32
- data/doc/release_notes/5.45.0.txt +0 -34
- data/doc/release_notes/5.46.0.txt +0 -87
- data/doc/release_notes/5.47.0.txt +0 -59
- data/doc/release_notes/5.48.0.txt +0 -14
- data/doc/release_notes/5.49.0.txt +0 -59
- data/doc/release_notes/5.5.0.txt +0 -61
- data/doc/release_notes/5.50.0.txt +0 -78
- data/doc/release_notes/5.51.0.txt +0 -47
- data/doc/release_notes/5.52.0.txt +0 -87
- data/doc/release_notes/5.53.0.txt +0 -23
- data/doc/release_notes/5.54.0.txt +0 -27
- data/doc/release_notes/5.55.0.txt +0 -21
- data/doc/release_notes/5.56.0.txt +0 -51
- data/doc/release_notes/5.57.0.txt +0 -23
- data/doc/release_notes/5.58.0.txt +0 -31
- data/doc/release_notes/5.59.0.txt +0 -73
- data/doc/release_notes/5.6.0.txt +0 -31
- data/doc/release_notes/5.60.0.txt +0 -22
- data/doc/release_notes/5.61.0.txt +0 -43
- data/doc/release_notes/5.62.0.txt +0 -132
- data/doc/release_notes/5.63.0.txt +0 -33
- data/doc/release_notes/5.64.0.txt +0 -50
- data/doc/release_notes/5.65.0.txt +0 -21
- data/doc/release_notes/5.66.0.txt +0 -24
- data/doc/release_notes/5.67.0.txt +0 -32
- data/doc/release_notes/5.68.0.txt +0 -61
- data/doc/release_notes/5.69.0.txt +0 -26
- data/doc/release_notes/5.7.0.txt +0 -108
- data/doc/release_notes/5.70.0.txt +0 -35
- data/doc/release_notes/5.71.0.txt +0 -21
- data/doc/release_notes/5.72.0.txt +0 -33
- data/doc/release_notes/5.73.0.txt +0 -66
- data/doc/release_notes/5.74.0.txt +0 -45
- data/doc/release_notes/5.75.0.txt +0 -35
- data/doc/release_notes/5.76.0.txt +0 -86
- data/doc/release_notes/5.77.0.txt +0 -63
- data/doc/release_notes/5.78.0.txt +0 -67
- data/doc/release_notes/5.79.0.txt +0 -28
- data/doc/release_notes/5.8.0.txt +0 -170
- data/doc/release_notes/5.80.0.txt +0 -40
- data/doc/release_notes/5.81.0.txt +0 -31
- data/doc/release_notes/5.82.0.txt +0 -61
- data/doc/release_notes/5.83.0.txt +0 -56
- data/doc/release_notes/5.9.0.txt +0 -99
- data/doc/schema_modification.rdoc +0 -679
- data/doc/security.rdoc +0 -443
- data/doc/sharding.rdoc +0 -286
- data/doc/sql.rdoc +0 -648
- data/doc/testing.rdoc +0 -204
- data/doc/thread_safety.rdoc +0 -15
- data/doc/transactions.rdoc +0 -250
- data/doc/validations.rdoc +0 -558
- data/doc/virtual_rows.rdoc +0 -265
data/doc/security.rdoc
DELETED
@@ -1,443 +0,0 @@
|
|
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::Dataset.def_sql_method
|
20
|
-
* Sequel::JDBC.load_driver
|
21
|
-
* Sequel::Plugins.def_dataset_methods
|
22
|
-
* Sequel::Dataset.prepared_statements_module (private)
|
23
|
-
* Sequel::SQL::Expression.to_s_method (private)
|
24
|
-
|
25
|
-
As long as you don't call those with user input, you should not be
|
26
|
-
vulnerable to code execution.
|
27
|
-
|
28
|
-
== SQL Injection
|
29
|
-
|
30
|
-
The primary security concern in SQL database libraries is SQL injection.
|
31
|
-
Because Sequel promotes using ruby objects for SQL concepts instead
|
32
|
-
of raw SQL, it is less likely to be vulnerable to SQL injection.
|
33
|
-
However, because Sequel still makes it easy to use raw SQL, misuse of the
|
34
|
-
library can result in SQL injection in your application.
|
35
|
-
|
36
|
-
There are basically two kinds of possible SQL injections in Sequel:
|
37
|
-
|
38
|
-
* SQL code injections
|
39
|
-
* SQL identifier injections
|
40
|
-
|
41
|
-
=== SQL Code Injections
|
42
|
-
|
43
|
-
==== Full SQL Strings
|
44
|
-
|
45
|
-
Some Sequel methods are designed to execute raw SQL strings, including:
|
46
|
-
|
47
|
-
* Sequel::Database#execute
|
48
|
-
* Sequel::Database#execute_ddl
|
49
|
-
* Sequel::Database#execute_dui
|
50
|
-
* Sequel::Database#execute_insert
|
51
|
-
* Sequel::Database#run
|
52
|
-
* Sequel::Database#<<
|
53
|
-
* Sequel::Dataset#fetch_rows
|
54
|
-
* Sequel::Dataset#with_sql_all
|
55
|
-
* Sequel::Dataset#with_sql_delete
|
56
|
-
* Sequel::Dataset#with_sql_each
|
57
|
-
* Sequel::Dataset#with_sql_first
|
58
|
-
* Sequel::Dataset#with_sql_insert
|
59
|
-
* Sequel::Dataset#with_sql_single_value
|
60
|
-
* Sequel::Dataset#with_sql_update
|
61
|
-
|
62
|
-
Here are some examples of use:
|
63
|
-
|
64
|
-
DB.execute 'SQL'
|
65
|
-
DB.execute_ddl 'SQL'
|
66
|
-
DB.execute_dui 'SQL'
|
67
|
-
DB.execute_insert 'SQL'
|
68
|
-
DB.run 'SQL'
|
69
|
-
DB << 'SQL'
|
70
|
-
DB.fetch_rows('SQL'){|row| }
|
71
|
-
DB.dataset.with_sql_all('SQL')
|
72
|
-
DB.dataset.with_sql_delete('SQL')
|
73
|
-
DB.dataset.with_sql_each('SQL'){|row| }
|
74
|
-
DB.dataset.with_sql_first('SQL')
|
75
|
-
DB.dataset.with_sql_insert('SQL')
|
76
|
-
DB.dataset.with_sql_single_value('SQL')
|
77
|
-
DB.dataset.with_sql_update('SQL')
|
78
|
-
|
79
|
-
If you pass a string to these methods that is derived from user input, you open
|
80
|
-
yourself up to SQL injection. These methods are not designed to work at all
|
81
|
-
with user input. If you must call them with user input, you should escape the
|
82
|
-
user input manually via Sequel::Database#literal. Example:
|
83
|
-
|
84
|
-
DB.run "SOME SQL #{DB.literal(params[:user].to_s)}"
|
85
|
-
|
86
|
-
==== Full SQL Strings, With Possible Placeholders
|
87
|
-
|
88
|
-
Other Sequel methods are designed to support execution of raw SQL strings that may contain placeholders:
|
89
|
-
|
90
|
-
* Sequel::Database#[]
|
91
|
-
* Sequel::Database#fetch
|
92
|
-
* Sequel::Dataset#with_sql
|
93
|
-
|
94
|
-
Here are some examples of use:
|
95
|
-
|
96
|
-
DB['SQL'].all
|
97
|
-
DB.fetch('SQL').all
|
98
|
-
DB.dataset.with_sql('SQL').all
|
99
|
-
|
100
|
-
With these methods you should use placeholders, in which case Sequel automatically escapes the input:
|
101
|
-
|
102
|
-
DB['SELECT * FROM foo WHERE bar = ?', params[:user].to_s]
|
103
|
-
|
104
|
-
==== Manually Created Literal Strings
|
105
|
-
|
106
|
-
Sequel generally treats ruby strings as SQL strings (escaping them correctly), and
|
107
|
-
not as raw SQL. However, you can convert a ruby string to a literal string, and
|
108
|
-
Sequel will then treat it as raw SQL. This is typically done through
|
109
|
-
Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit].
|
110
|
-
|
111
|
-
Sequel.lit('a')
|
112
|
-
|
113
|
-
Using Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit] to turn a ruby string into a literal string results
|
114
|
-
in SQL injection if the string is derived from user input. With both of these
|
115
|
-
methods, the strings can contain placeholders, which you can use to safely include
|
116
|
-
user input inside a literal string:
|
117
|
-
|
118
|
-
Sequel.lit('a = ?', params[:user_id].to_s)
|
119
|
-
|
120
|
-
Even though they have similar names, note that Sequel::Database#literal operates very differently from
|
121
|
-
String#lit or Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit].
|
122
|
-
Sequel::Database#literal is for taking any supported object,
|
123
|
-
and getting an SQL representation of that object, while
|
124
|
-
String#lit or Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit] are for treating
|
125
|
-
a ruby string as raw SQL. For example:
|
126
|
-
|
127
|
-
DB.literal(Date.today) # "'2013-03-22'"
|
128
|
-
DB.literal('a') # "'a'"
|
129
|
-
DB.literal(Sequel.lit('a')) # "a"
|
130
|
-
DB.literal(a: 'a') # "(\"a\" = 'a')"
|
131
|
-
DB.literal(a: Sequel.lit('a')) # "(\"a\" = a)"
|
132
|
-
|
133
|
-
==== SQL Filter Fragments
|
134
|
-
|
135
|
-
Starting in Sequel 5, Sequel does not automatically convert plain strings to
|
136
|
-
literal strings in typical code. Instead, you can use Sequel.lit to
|
137
|
-
create literal strings:
|
138
|
-
|
139
|
-
Sequel.lit("name > 'A'")
|
140
|
-
|
141
|
-
To safely include user input as part of an SQL filter fragment, use Sequel.lit
|
142
|
-
with placeholders:
|
143
|
-
|
144
|
-
DB[:table].where(Sequel.lit("name > ?", params[:id].to_s)) # Safe
|
145
|
-
|
146
|
-
Be careful to never call Sequel.lit where the first argument is derived from
|
147
|
-
user input.
|
148
|
-
|
149
|
-
There are a few uncommon cases where Sequel will still convert
|
150
|
-
plain strings to literal strings.
|
151
|
-
|
152
|
-
==== SQL Fragment passed to Dataset#lock_style and Model#lock!
|
153
|
-
|
154
|
-
The Sequel::Dataset#lock_style and Sequel::Model#lock! methods also treat
|
155
|
-
an input string as SQL code. These methods should not be called with user input.
|
156
|
-
|
157
|
-
DB[:table].lock_style(params[:id]) # SQL injection!
|
158
|
-
Album.first.lock!(params[:id]) # SQL injection!
|
159
|
-
|
160
|
-
==== SQL Type Names
|
161
|
-
|
162
|
-
In general, most places where Sequel needs to use an SQL type that should
|
163
|
-
be specified by the user, it allows you to use a ruby string, and that
|
164
|
-
string is used verbatim as the SQL type. You should not use user input
|
165
|
-
for type strings.
|
166
|
-
|
167
|
-
DB[:table].select(Sequel.cast(:a, params[:id])) # SQL injection!
|
168
|
-
|
169
|
-
==== SQL Function Names
|
170
|
-
|
171
|
-
In most cases, Sequel does not quote SQL function names. You should not use
|
172
|
-
user input for function names.
|
173
|
-
|
174
|
-
DB[:table].select(Sequel.function(params[:id])) # SQL injection!
|
175
|
-
|
176
|
-
==== SQL Window Frames
|
177
|
-
|
178
|
-
For backwards compatibility, Sequel supports regular strings in the
|
179
|
-
window function :frame option, which will be treated as a literal string:
|
180
|
-
|
181
|
-
DB[:table].select{fun(arg).over(frame: 'SQL Here')}
|
182
|
-
|
183
|
-
You should make sure the frame argument is not derived from user input,
|
184
|
-
or switch to using a hash as the :frame option value.
|
185
|
-
|
186
|
-
==== auto_literal_strings extension
|
187
|
-
|
188
|
-
If the auto_literal_strings extension is used for backwards compatibility,
|
189
|
-
then Sequel will treat plain strings as literal strings if they are used
|
190
|
-
as the first argument to a filtering method. This can lead to SQL
|
191
|
-
injection:
|
192
|
-
|
193
|
-
DB[:table].where("name > #{params[:id].to_s}")
|
194
|
-
# SQL injection when using auto_literal_strings extension
|
195
|
-
|
196
|
-
If you are using the auto_literal_strings extension, you need to be very careful,
|
197
|
-
as the following methods will treat a plain string given as the first argument
|
198
|
-
as a literal string:
|
199
|
-
|
200
|
-
* Sequel::Dataset#where
|
201
|
-
* Sequel::Dataset#having
|
202
|
-
* Sequel::Dataset#filter
|
203
|
-
* Sequel::Dataset#exclude
|
204
|
-
* Sequel::Dataset#exclude_having
|
205
|
-
* Sequel::Dataset#or
|
206
|
-
* Sequel::Dataset#first
|
207
|
-
* Sequel::Dataset#last
|
208
|
-
* Sequel::Dataset#[]
|
209
|
-
|
210
|
-
Even stuff that looks like it may be safe isn't:
|
211
|
-
|
212
|
-
DB[:table].first(params[:num_rows])
|
213
|
-
# SQL injection when using auto_literal_strings extension
|
214
|
-
|
215
|
-
The Model.find[rdoc-ref:Sequel::Model::ClassMethods#find] and
|
216
|
-
Model.find_or_create[rdoc-ref:Sequel::Model::ClassMethods#find_or_create]
|
217
|
-
class methods will also treat string arguments as literal strings if the
|
218
|
-
auto_literal_strings extension is used:
|
219
|
-
|
220
|
-
Album.find(params[:id])
|
221
|
-
# SQL injection when using auto_literal_strings extension
|
222
|
-
|
223
|
-
Similar to the filter methods, the auto_literal_strings extension
|
224
|
-
also makes Sequel::Dataset#update treats a string argument as raw SQL:
|
225
|
-
|
226
|
-
DB[:table].update("column = 1")
|
227
|
-
|
228
|
-
So you should not do:
|
229
|
-
|
230
|
-
DB[:table].update(params[:changes])
|
231
|
-
# SQL injection when using auto_literal_strings extension
|
232
|
-
|
233
|
-
or:
|
234
|
-
|
235
|
-
DB[:table].update("column = #{params[:value].to_s}")
|
236
|
-
# SQL injection when using auto_literal_strings extension
|
237
|
-
|
238
|
-
Instead, you should do:
|
239
|
-
|
240
|
-
DB[:table].update(column: params[:value].to_s) # Safe
|
241
|
-
|
242
|
-
Because using the auto_literal_strings extension makes SQL injection
|
243
|
-
so much eaiser, it is recommended to not use it, and instead
|
244
|
-
use Sequel.lit with placeholders.
|
245
|
-
|
246
|
-
=== SQL Identifier Injections
|
247
|
-
|
248
|
-
Usually, Sequel treats ruby symbols as SQL identifiers, and ruby
|
249
|
-
strings as SQL strings. However, there are some parts of Sequel
|
250
|
-
that treat ruby strings as SQL identifiers if an SQL string would
|
251
|
-
not make sense in the same context.
|
252
|
-
|
253
|
-
For example, Sequel::Database#from and Sequel::Dataset#from will treat a string as
|
254
|
-
a table name:
|
255
|
-
|
256
|
-
DB.from('t') # SELECT * FROM "t"
|
257
|
-
|
258
|
-
Another place where Sequel treats ruby strings as identifiers are
|
259
|
-
the Sequel::Dataset#insert and Sequel::Dataset#update methods:
|
260
|
-
|
261
|
-
DB[:t].update('b'=>1) # UPDATE "t" SET "b" = 1
|
262
|
-
DB[:t].insert('b'=>1) # INSERT INTO "t" ("b") VALUES (1)
|
263
|
-
|
264
|
-
Note how the identifier is still quoted in these cases. Sequel quotes identifiers by default
|
265
|
-
on most databases. However, it does not quote identifiers by default on DB2.
|
266
|
-
On those databases using an identifier derived from user input can lead to SQL injection.
|
267
|
-
Similarly, if you turn off identifier quoting manually on other databases, you open yourself
|
268
|
-
up to SQL injection if you use identifiers derived from user input.
|
269
|
-
|
270
|
-
When Sequel quotes identifiers, using an identifier derived from user input does not lead to
|
271
|
-
SQL injection, since the identifiers are also escaped when quoting.
|
272
|
-
Exceptions to this are Oracle (can't escape <tt>"</tt>) and Microsoft Access
|
273
|
-
(can't escape <tt>]</tt>).
|
274
|
-
|
275
|
-
In general, even if doesn't lead to SQL Injection, you should avoid using identifiers
|
276
|
-
derived from user input unless absolutely necessary.
|
277
|
-
|
278
|
-
Sequel also allows you to create identifiers using
|
279
|
-
Sequel.identifier[rdoc-ref:Sequel::SQL::Builders#identifier] for plain identifiers,
|
280
|
-
Sequel.qualify[rdoc-ref:Sequel::SQL::Builders#qualify] and
|
281
|
-
Sequel::SQL::Indentifier#[][rdoc-ref:Sequel::SQL::QualifyingMethods#[]] for qualified identifiers, and
|
282
|
-
Sequel.as[rdoc-ref:Sequel::SQL::Builders#as] for aliased expressions. So if you
|
283
|
-
pass any of those values derived from user input, you are dealing with the same scenario.
|
284
|
-
|
285
|
-
Note that the issues with SQL identifiers do not just apply to places where
|
286
|
-
strings are used as identifiers, they also apply to all places where Sequel
|
287
|
-
uses symbols as identifiers. However, if you are creating symbols from user input,
|
288
|
-
you at least have a denial of service vulnerability in ruby <2.2, and possibly a
|
289
|
-
more serious vulnerability.
|
290
|
-
|
291
|
-
Note that many Database schema modification methods (e.g. create_table, add_column)
|
292
|
-
also allow for SQL identifier injections, and possibly also SQL code injections.
|
293
|
-
These methods should never be called with user input.
|
294
|
-
|
295
|
-
== Denial of Service
|
296
|
-
|
297
|
-
Sequel converts some strings to symbols. Because symbols in ruby <2.2 are not
|
298
|
-
garbage collected, if the strings that are converted to symbols are
|
299
|
-
derived from user input, you have a denial of service vulnerability due to
|
300
|
-
memory exhaustion.
|
301
|
-
|
302
|
-
The strings that Sequel converts to symbols are generally not derived
|
303
|
-
from user input, so Sequel in general is not vulnerable to this. However,
|
304
|
-
users should be aware of the cases in which Sequel creates symbols, so
|
305
|
-
they do not introduce a vulnerability into their application.
|
306
|
-
|
307
|
-
=== Column Names/Aliases
|
308
|
-
|
309
|
-
Sequel returns SQL result sets as an array of hashes with symbol keys. The
|
310
|
-
keys are derived from the name that the database server gives the column. These
|
311
|
-
names are generally static. For example:
|
312
|
-
|
313
|
-
SELECT column FROM table
|
314
|
-
|
315
|
-
The database will generally use "column" as the name in the result set.
|
316
|
-
|
317
|
-
If you use an alias:
|
318
|
-
|
319
|
-
SELECT column AS alias FROM table
|
320
|
-
|
321
|
-
The database will generally use "alias" as the name in the result set. So
|
322
|
-
if you allow the user to control the alias name:
|
323
|
-
|
324
|
-
DB[:table].select(:column.as(params[:alias]))
|
325
|
-
|
326
|
-
Then you can have a denial of service vulnerability. In general, such a vulnerability
|
327
|
-
is unlikely, because you are probably indexing into the returned hash(es) by name,
|
328
|
-
and if an alias was used and you didn't expect it, your application wouldn't work.
|
329
|
-
|
330
|
-
=== Database Connection Options
|
331
|
-
|
332
|
-
All database connection options are converted to symbols. For a
|
333
|
-
connection URL, the keys are generally fixed, but the scheme is turned
|
334
|
-
into a symbol and the query option keys are used as connection option
|
335
|
-
keys, so they are converted to symbols as well. For example:
|
336
|
-
|
337
|
-
postgres://host/database?option1=foo&option2=bar
|
338
|
-
|
339
|
-
Will result in :postgres, :option1, and :option2 symbols being created.
|
340
|
-
|
341
|
-
Certain option values are also converted to symbols. In the general case,
|
342
|
-
the sql_log_level option value is, but some adapters treat additional
|
343
|
-
options similarly.
|
344
|
-
|
345
|
-
This is not generally a risk unless you are allowing the user to control
|
346
|
-
the connection URLs or are connecting to arbitrary databases at runtime.
|
347
|
-
|
348
|
-
== Mass Assignment
|
349
|
-
|
350
|
-
Mass assignment is the practice of passing a hash of columns and values
|
351
|
-
to a single method, and having multiple column values for a given object set
|
352
|
-
based on the content of the hash.
|
353
|
-
The security issue here is that mass assignment may allow the user to
|
354
|
-
set columns that you didn't intend to allow.
|
355
|
-
|
356
|
-
The Model#set[rdoc-ref:Sequel::Model::InstanceMethods#set] and Model#update[rdoc-ref:Sequel::Model::InstanceMethods#update] methods do mass
|
357
|
-
assignment. The default configuration of Sequel::Model allows all model
|
358
|
-
columns except for the primary key column(s) to be set via mass assignment.
|
359
|
-
|
360
|
-
Example:
|
361
|
-
|
362
|
-
album = Album.new
|
363
|
-
album.set(params[:album]) # Mass Assignment
|
364
|
-
|
365
|
-
Both Model.new[rdoc-ref:Sequel::Model::InstanceMethods::new] and Model.create[rdoc-ref:Sequel::Model::ClassMethods#create]
|
366
|
-
call Model#set[rdoc-ref:Sequel::Model::InstanceMethods#set] internally, so
|
367
|
-
they also allow mass assignment:
|
368
|
-
|
369
|
-
Album.new(params[:album]) # Mass Assignment
|
370
|
-
Album.create(params[:album]) # Mass Assignment
|
371
|
-
|
372
|
-
When the argument is derived from user input, instead of these methods, it is encouraged to either use
|
373
|
-
Model#set_fields[rdoc-ref:Sequel::Model::InstanceMethods#set_fields] or
|
374
|
-
Model#update_fields[rdoc-ref:Sequel::Model::InstanceMethods#update_fields],
|
375
|
-
which allow you to specify which fields to allow on a per-call basis. This
|
376
|
-
pretty much eliminates the chance that the user will be able to set a column
|
377
|
-
you did not intend to allow:
|
378
|
-
|
379
|
-
album.set_fields(params[:album], [:name, :copies_sold])
|
380
|
-
album.update_fields(params[:album], [:name, :copies_sold])
|
381
|
-
|
382
|
-
These two methods iterate over the second argument (+:name+ and +:copies_sold+ in
|
383
|
-
this example) instead of iterating over the entries in the first argument
|
384
|
-
(<tt>params[:album]</tt> in this example).
|
385
|
-
|
386
|
-
If you want to override the columns that Model#set[rdoc-ref:Sequel::Model::InstanceMethods#set]
|
387
|
-
allows by default during mass assignment, you can use the whitelist_security plugin, then call
|
388
|
-
the set_allowed_columns class method.
|
389
|
-
|
390
|
-
Album.plugin :whitelist_security
|
391
|
-
Album.set_allowed_columns(:name, :copies_sold)
|
392
|
-
Album.create(params[:album]) # Only name and copies_sold set
|
393
|
-
|
394
|
-
Being explicit on a per-call basis using the set_fields and update_fields methods is recommended
|
395
|
-
instead of using the whitelist_security plugin and setting a global whitelist.
|
396
|
-
|
397
|
-
For more details on the mass assignment methods, see the {Mass Assignment Guide}[rdoc-ref:doc/mass_assignment.rdoc].
|
398
|
-
|
399
|
-
== General Parameter Handling
|
400
|
-
|
401
|
-
This issue isn't necessarily specific to Sequel, but it is a good general practice.
|
402
|
-
If you are using values derived from user input, it is best to be explicit about
|
403
|
-
their type. For example:
|
404
|
-
|
405
|
-
Album.where(id: params[:id])
|
406
|
-
|
407
|
-
is probably a bad idea. Assuming you are using a web framework, <tt>params[:id]</tt> could
|
408
|
-
be a string, an array, a hash, nil, or potentially something else.
|
409
|
-
|
410
|
-
Assuming that +id+ is an integer field, you probably want to do:
|
411
|
-
|
412
|
-
Album.where(id: params[:id].to_i)
|
413
|
-
|
414
|
-
If you are looking something up by name, you should try to enforce the value to be
|
415
|
-
a string:
|
416
|
-
|
417
|
-
Album.where(name: params[:name].to_s)
|
418
|
-
|
419
|
-
If you are trying to use an IN clause with a list of id values based on input provided
|
420
|
-
on a web form:
|
421
|
-
|
422
|
-
Album.where(id: params[:ids].to_a.map(&:to_i))
|
423
|
-
|
424
|
-
Basically, be as explicit as possible. While there aren't any known security issues
|
425
|
-
in Sequel when you do:
|
426
|
-
|
427
|
-
Album.where(id: params[:id])
|
428
|
-
|
429
|
-
It allows the attacker to choose to do any of the following queries:
|
430
|
-
|
431
|
-
id IS NULL # nil
|
432
|
-
id = '1' # '1'
|
433
|
-
id IN ('1', '2', '3') # ['1', '2', '3']
|
434
|
-
id = ('a' = 'b') # {'a'=>'b'}
|
435
|
-
id = ('a' IN ('a', 'b') AND 'c' = '') # {'a'=>['a', 'b'], 'c'=>''}
|
436
|
-
|
437
|
-
While none of those allow for SQL injection, it's possible that they
|
438
|
-
might have an issue in your application. For example, a long array
|
439
|
-
or deeply nested hash might cause the database to have to do a lot of
|
440
|
-
work that could be avoided.
|
441
|
-
|
442
|
-
In general, it's best to let the attacker control as little as possible,
|
443
|
-
and explicitly specifying types helps a great deal there.
|