Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Dangerous query method" deprecation warning matches random() function call #32995

Closed
searls opened this issue May 25, 2018 · 62 comments · May be fixed by #33330
Closed

"Dangerous query method" deprecation warning matches random() function call #32995

searls opened this issue May 25, 2018 · 62 comments · May be fixed by #33330

Comments

@searls
Copy link
Contributor

@searls searls commented May 25, 2018

Steps to reproduce

Post.all.order('random()')

Expected behavior

It should silently work, invoking postgres's random() function for determining order

Actual behavior

I get this warning:

DEPRECATION WARNING: Dangerous query method (method whose arguments are used as raw SQL) called with non-attribute argument(s): "random()". Non-attribute arguments will be disallowed in Rails 6.0. This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql(). (called from …)

System configuration

Rails version: 5.2.0

Ruby version: 2.5.1p57

Using postgres

@utilum
Copy link
Contributor

@utilum utilum commented May 26, 2018

This is expected. It's trying to tell you to do like so: Post.all.order(Arel.sql('random()')).

See 310c3a8

@searls
Copy link
Contributor Author

@searls searls commented May 29, 2018

I'm sorry if I'm being dense, but my reaction on this being closed was to feel dubious of what value Arel.sql('random()') is providing me as a user in this case.

  • If wrapping any potentially-unsafe thing in Arel.sql(…) is simply the equivalent of a --force confirmation to protect users from themselves, it seems contrary to Rails' ethos of preferring terse expression even if it means trusting people with sharp tools.

  • If instead the wrapping Arel.sql(…) call serves some technical purpose, couldn't the framework detect this and do it for users?

If I'm right in assuming it's the former, then this sort of change reminds me a bit of strong_params, except instead of coercing users to re-think mass data access, the most likely outcome here will be for people to get angry when Rails 6.0 breaks a bunch of queries and then blindly sprinkle in Arel.sql() calls all over the place to make the errors go away. And in that case, wouldn't the end result will be messier codebases without making queries more secure (prompting a Rails 7 Arel.no_really_sql(Arel.sql(…)) wrapper)?

I just stumbled upon this, so it's likely I'm off-base here, but this seems like a default I'd rather be hidden behind an opt-in config option for people who want to prevent queries like this to be generated conveniently.

@eileencodes
Copy link
Member

@eileencodes eileencodes commented May 29, 2018

cc/ @mastahyeti since you implemented the original implementation what are your thoughts on this issue? It does seem a bit heavy handed for users of Rails.

@rafaelfranca
Copy link
Member

@rafaelfranca rafaelfranca commented May 29, 2018

Is not the deprecation message making users to re-think SQL injection by telling them to review the query to see if there is user input on it and if not call Arel.sql?

This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql().

That is the reason for this change. If users are blindly calling Arel.sql, well, that is where the provide sharp tools enters, it is their call.

I don't believe this should be opt-in. I strong believe we should not allow people to opt-in security because most likely they will not. Opt-out is an option, if you want to deal with this yourself I think you should be allowed. The good news is: it is already possible to opt-out using allow_unsafe_raw_sql. (it is missing documentation and I'll fix this right now).

If the message is not making users to re-think SQL injection, so we should improve it.

In summary, in my opinion, the current implementation we have is the one that maps better what we need. We need people to rethink SQL injection by not allowing untrusted values in methods that are possible to inject SQL.

@btoews
Copy link
Contributor

@btoews btoews commented May 29, 2018

Hi @searls. The issue #27947 was intended to address was

Post.order(params[:order])

which is a common pattern in Rails app, unfortunately leading to SQL injection.

... it seems contrary to Rails' ethos of preferring terse expression even if it means trusting people with sharp tools.

I'm not sure this has been my experience of the Rails ethos when it comes to security. While Rails doesn't have the best history of being "secure by default", some effort is made to protect users from themselves. Think html_safe.

The protections added in #27947 will likely add some friction for some users, but I don't think it will be so dire. The #pluck and #order APIs are usually passed column names. The decision was made to draw the line at allowing column_name or table_name.column_name, but I can see allowing some additional whitelist of "safe" functions if this becomes too much of a burden.

And in that case, wouldn't the end result will be messier codebases without making queries more secure (prompting a Rails 7 Arel.no_really_sql(Arel.sql(…)) wrapper)?

As someone who works on securing a large Rails app, I can tell you that having to audit for .order(Arel.sql(...)) calls is much easier than having to audit all .order(...) calls. It has been my experience that developers rarely have to pass Arel.sql(...) to these APIs because they rarely are wanting to pass anything other than a column name.

@searls
Copy link
Contributor Author

@searls searls commented May 29, 2018

Thanks, @mastahyeti, for the added context. Knowing this applies just to a handful of Arel's methods is definitely helpful and makes this seem less dire of a concern than if it applied to any string passed into any of them.

I'm also glad to hear you'd be open to a whitelist of common functions, as I think you'd pretty quickly only see this error in pretty unusual circumstances.

[I'm curious, too, whether there's some other heuristic (a regex?) in any of these cases that could (performantly enough) establish confidence that nothing harmful could have been injected. But this is a well worn problem and I'm a security noob, so I'm sure that might be a silly suggestion.]

@al2o3cr
Copy link
Contributor

@al2o3cr al2o3cr commented Jun 1, 2018

@searls regarding a regex, IIRC the core team's position is "don't write a SQL parser" :)

@SamSaffron
Copy link
Contributor

@SamSaffron SamSaffron commented Jun 6, 2018

I would like to echo my pain here upgrading Discourse to Rails 5.2. which is in progress

discourse/discourse@5373e84 (link will die at some point)

I would strongly prefer if we could just turn off this feature for Discourse.

Especially since I now have this kind of crazy.

 sent.where("created_at BETWEEN ? AND ?", start_date, end_date)
       .group("DATE(created_at)")
-      .order("DATE(created_at)")
+      .order(Arel.sql "DATE(created_at)")
       .count

This is very frustrating on a couple of counts

  1. I need to now train all our devs to remember that #pluck and #order are special snowflakes

  2. We are now allocating one more object (and one extra method call) unless we pull these Arel.sql magic things into a const or something

Would Rails core be open to making this new restriction configurable?

@SamSaffron
Copy link
Contributor

@SamSaffron SamSaffron commented Jun 6, 2018

@rafaelfranca reading through the source I am not seeing how this is optional?

It appears allow_unsafe_raw_sql can not be set to true for a bypass, it only checks if it is :deprecated or not

Are you open to a PR that allows this to be set to true and then we add a bypass prior to calling enforce_raw_sql_whitelist?

This also helps us cause we get to cut down on the whitelist match stuff for a very minor speed bump.

@rafaelfranca
Copy link
Member

@rafaelfranca rafaelfranca commented Jun 6, 2018

I think we should allow it. I'm happy with PR.

@matthewd
Copy link
Member

@matthewd matthewd commented Jun 6, 2018

I can help with the asymmetry: group should almost certainly apply this rule too in a future version. where is a conscious exclusion, but others may still need consideration.

In your commit you seem to have added Arel.sql to a number of order calls that don't require it: what we're "--forcing" here is that a method which is generally passed a column name, is intentionally instead being passed a more complex SQL fragment. (e.g. 5 of 7 instances in post.rb)

We originally considered allowing allow_unsafe_raw_sql to disable the check entirely, but ultimately decided against it because the warning already provides a 'disabled' mode in the sense of "still works like 5.1"; a "works without complaining" would be unhelpful long term, because like all upgrade tools this setting will eventually go away -- we don't want to carry an opt-out conditional forever. Global settings also make life more complicated for gem authors, for example.

Shaving a flat one or two short-lived-object allocations off a full "build a query, run it, wait for the network, load the results" cycle doesn't seem performantly exciting to me. If Ruby had a way to identify a shared/static string, it'd be neat to cache their SQL-node forms, but I know of no such thing.

These methods are already pretty snowflaky: they mostly look like they take a column name, but sometimes they take an SQL fragment, but not if you want ? placeholders, but there's actually a special spelling to do that too.

If you really want to kill it you can presumably monkey-patch enforce_raw_sql_whitelist to a no-op, but I'm having a hard time buying a "developer ergonomics should trump a security/safety mechanism" argument the same week as "performance should trump developer ergonomics".

@SamSaffron
Copy link
Contributor

@SamSaffron SamSaffron commented Jun 6, 2018

I'm having a hard time buying a "developer ergonomics should trump a security/safety mechanism" argument the same week as "performance should trump developer ergonomics".

This is an unfair stab, especially since I never said that, all I said is that I wish performance was somehow mentioned in the Rails doctrine, even in a "Kaizen" form where we strive to make Rails faster every release. A faster Rails encourages developers to upgrade to the latest version. It encourages people to say "yes" to features so it overall "improves developer happiness".

Back to the issue at hand, I am monkey patching out the method (I just did that in a commit to Discourse) I will consider moving to the Rails-way here if you can demonstrate in a file or 2 how this does not cause the code to suffer and become ugly. The training vector really needs consideration here especially around the disparity with other methods.

Overall a custom whitelist may be a way to unblock us here cause we could then explicitly whitelist some of this stuff. Then use something like LRURedux to bypass this evaluation for strings we know are good or something like that. Especially if stuff like order('something ?', something) automatically whitelists. I am concerned about running every order string via 100 regexes long term but there may be optimization.

A big concern here is that the "escape hatch" proposed in the deprecation message is not ideal. You see it enough times on upgrade and you take an atomic approach to adding it even where it is not needed. My screen filled up with add Arel.sql so I just blanket added it everywhere to make the spec suite stop nagging. If I know that date({column}) is always going to be good I should be able to just blanket allow it.

In other news Discourse does not appear to be any slower post the 5.2 upgrade (performance seems on par with the bench) so 🎊

@ignisf
Copy link

@ignisf ignisf commented Jun 6, 2018

Hello,

So the assumption here is that there's widespread use of Model.order(<unsanitised params>). Could we not target the input of unsanitised params directly instead of assuming any string literal passed here is unsafe?

This would allow for a more precise warning to the users, too, or even an exception, stating that this invocation creates a vulnerability and is thus disallowed (see Model.where(params)).

Thinking about this, it leads me to a more general solution that allows param values to be marked as unsanitised?

@matthewd
Copy link
Member

@matthewd matthewd commented Jun 6, 2018

@ignisf the assumption is that it's a sufficiently widespread problem (and more generally, non-obvious danger in an API that looks like it's taking a column name) that someone made a website about it.

The stronger assumption, though, is that most calls to order are passing a simple column reference; much of the discussion in #27947 was around how to minimize the number of callers that would be affected by the warning.

It sounds like you're getting close to inventing string tainting, which has its own issues.

This case isn't really about rejecting user input, though -- it's about choosing which of two "identical"-looking APIs you intended to use.

A big concern here is that the "escape hatch" proposed in the deprecation message is not ideal. You see it enough times on upgrade and you take an atomic approach to adding it even where it is not needed.

I'm really not sure how much we can do about this. If it was as simple as making the change everywhere, we would've done it for you; the reason we're flooding your output with pointers to each individual call is that we want you to consider them individually.

If you want to alias rm="rm -f --no-preserve-root" the first time you actually need to rm /, you're free to do that... but I think most people will benefit from only disabling the safety when they need the unconstrained power.

Granted, Discourse has 5 instances of order-by-date... but those actually seem to have enough more in common (.where("<column> BETWEEN ? AND ?", x, y) .group("date(<column>)") .order("date(<column>)")) that they'd benefit more from a method extraction than a custom regexp.

The others all seem pretty impractical to define general rules for.

The code is expected to become incrementally uglier in places where it's already uglyish (SQL inside strings); I can't offer an example where it makes things prettier. The argument is only that it happens rarely enough that the safety trade-off is worth it.

an unfair stab

Quelle horreur. 😒

The Doctrine is David's description of his design philosophy. It also doesn't say anything about fixing bugs, for example. Most importantly, while its sentiment is weighed in considering how we present things to our users, it has no influence on how our volunteers spend their uncompensated free time.

@janko
Copy link
Contributor

@janko janko commented Jun 6, 2018

FWIW, Sequel introduced the same change in version 5.0. You now need to explicitly mark the SQL string as a "literal" (just like Arel.sql):

Post.order(Sequel.lit("random()"))

I consider this to be a positive change, so I support the same changes in Active Record. Jeremy Evans argued that, in addition to being secure by default, this change also makes security audits for SQL injection easier, because you can grep for Sequel.lit (in this case Arel.sql).

Sequel also provides an extension to disable this behaviour, and from what I understand it will be supported indefinitely. It would be good if Active Record would offer the same indefinitely, though I understand the wish to remove it at some point. It could be a module (a "concern", if you will) that gets included when the user enables the old functionality; that way these conditionals will be separated from the main code (that's how Sequel does it).

@SamSaffron
Copy link
Contributor

@SamSaffron SamSaffron commented Jun 6, 2018

I'm really not sure how much we can do about this. If it was as simple as making the change everywhere, we would've done it for you; the reason we're flooding your output with pointers to each individual call is that we want you to consider them individually.

Oh, one simple change that we could make here is only alerting in STDERR the first time the call site is detected, a very big pain here was that the same line (which was covered many times) kept on showing up over and over again on one test run.

The argument is only that it happens rarely enough that the safety trade-off is worth it.

It could be that I was a lot biased by having to through the entire app in one go, its just a lot of changes and we use SQL a lot.

So to summarize

  1. Alert only on first call site now to ease upgrade
  2. Enforce this more consistently group, joins and others so it correctly protects
  3. Consider perhaps a diff syntax for whitelisting

The idea around 3 would possibly be allowing:

.order('count(*) desc')
.safe

or maybe:

.unsafe_order(

not sure.

note: I was super careful not to mention string tainting, cause this is very much a feature I want erased from Ruby

@matthewd
Copy link
Member

@matthewd matthewd commented Jun 6, 2018

The logspam from a single frequently-hit deprecation is a fair point, though we should address it generally in the deprecation reporter. Personally, I think it feels really good when I make a single change and a few thousand warnings go away, but to each his own 😄

#27947 started at unsafe_order; Arel.sql felt more like HTML-escaping, and is consistent with the fact that everything ultimately passes through Arel anyway. (And correspondingly avoids us having to pass (value, safeness) pairs around everywhere internally.)

Are you trying to avoid the allocation here, or is it an aesthetic concern?

(Yeah, I only mention tainting as a self-evident explanation that that path is impractical.)

@SamSaffron
Copy link
Contributor

@SamSaffron SamSaffron commented Jun 6, 2018

Are you trying to avoid the allocation here, or is it an aesthetic concern?

I know it is mega weird coming from the "performance guy" but yeah this is mostly and aesthetic/symmetry concern, the extra single digit RVALUEs are not the end of the world.

@btoews
Copy link
Contributor

@btoews btoews commented Jun 6, 2018

I still have a preference for unsafe_raw_sql_order(). I think it's harder for a developer to type that method name without second guessing their approach.

@kamipo
Copy link
Member

@kamipo kamipo commented Jul 10, 2018

How about allowing frozen strings/objects to the whitelist since an user input is always a mutable string?
I've proposed the PR #33330.

@andynu
Copy link

@andynu andynu commented Sep 6, 2018

Override methods like unsafe_order and unsafe_group would be wonderful, and consistent with the security by default, but with a way around approach used with strong params and to_unsafe_h. Or a config to disable this safety check entirely.

For those of us that build atop legacy third party databases or have a need of simple functions it seems preferable to have a rails api method for those needs rather than having to jump over to the Arel api.

kamipo added a commit to kamipo/rails that referenced this issue Jun 9, 2019
…ring

Currently, almost all "Dangerous query method" warnings are false alarm.
As long as almost all the warnings are false alarm, developers think
"Let's ignore the warnings by using `Arel.sql()`, it actually is false
alarm in practice.", so I think we should effort to reduce false alarm
in order to make the warnings valuable.

This allows column name with function (e.g. `length(title)`) as safe SQL
string, which is very common false alarm pattern, even in the our
codebase.

Related 6c82b6c, 6607ecb, rails#36420.

Fixes rails#32995.
aldesantis added a commit to solidusio-contrib/solidus_gdpr that referenced this issue Sep 9, 2019
See rails/rails#32995 for more details.
@dlikhten
Copy link

@dlikhten dlikhten commented Sep 11, 2019

I am having issue with the deprecation warning, not 100% sure what is going on here:

.order(ActiveRecord::Base.sanitize_sql_for_order(['ABS(quantity_value - ?)', value.to_f]))

From the looks of it in the code, everything gets wrapped in Arel.sql however the deprecation warning persists.

Am I missing something? Is the suggested action of wrapping with Arel.sql not the correct thing to do now?

miry added a commit to jetthoughts/shipit-engine that referenced this issue Sep 12, 2019
There is a warning:

```
DEPRECATION WARNING: Dangerous query method (method whose arguments are used as raw SQL) called with non-attribute argument(s): "(undeployed_commits_count > 0) desc". Non-attribute arguments will be disallowed in Rails 6.1. This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql(). (called from index at app/controllers/shipit/stacks_controller.rb:12)
```

The warning discussed: rails/rails#32995
belgoros pushed a commit to belgoros/umed that referenced this issue Sep 17, 2019
DEPRECATION WARNING: Dangerous query method (method whose arguments are used as raw SQL) called with non-attribute argument(s): "random()". Non-attribute arguments will be disallowed in Rails 6.0. This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql(). (called from …).
See rails/rails#32995 for more discussions details.
@bewitchingme
Copy link

@bewitchingme bewitchingme commented Jan 17, 2020

Is not the deprecation message making users to re-think SQL injection by telling them to review the query to see if there is user input on it and if not call Arel.sql?

This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql().

That is the reason for this change. If users are blindly calling Arel.sql, well, that is where the provide sharp tools enters, it is their call.

I don't believe this should be opt-in. I strong believe we should not allow people to opt-in security because most likely they will not. Opt-out is an option, if you want to deal with this yourself I think you should be allowed. The good news is: it is already possible to opt-out using allow_unsafe_raw_sql. (it is missing documentation and I'll fix this right now).

If the message is not making users to re-think SQL injection, so we should improve it.

In summary, in my opinion, the current implementation we have is the one that maps better what we need. We need people to rethink SQL injection by not allowing untrusted values in methods that are possible to inject SQL.

The trouble is this deprecation warning is displayed even when no SQL-injection is potentially in play. I experienced this too, consider the following:

.order('extract(year from imports.end_date) ASC, reports.number_of_months_covered ASC, reports.annual_overnight_visitation_type_id ASC')

No value is being passed to this method, but somehow I get this deprecation warning over use of PGSQL extract?

This makes me feel like the AR ORM is moving towards the way of thinking of Eloquent, where we have to hand hold the ORM each step of the way. I agree with the sharp tools, developer beware methodology; especially considering that this is a departure from the way Rails has always done this.

@ryanb
Copy link
Contributor

@ryanb ryanb commented Mar 9, 2020

I am having issue with the deprecation warning, not 100% sure what is going on here:

.order(ActiveRecord::Base.sanitize_sql_for_order(['ABS(quantity_value - ?)', value.to_f]))

From the looks of it in the code, everything gets wrapped in Arel.sql however the deprecation warning persists.

Am I missing something? Is the suggested action of wrapping with Arel.sql not the correct thing to do now?

It's true sanitize_sql_for_order wraps the result in Arel.sql but the deprecation warning is happening before the internal call to Arel.sql. To resolve this you can call Arel.sql on the string you pass in. Also you can bypass sanitize_sql_for_order and pass the array directly to order.

.order([Arel.sql('ABS(quantity_value - ?)'), value.to_f])
kamipo added a commit to kamipo/rails that referenced this issue Apr 2, 2020
rails#27947 was intended to address `Post.order(params[:order])` which
leading to SQL injection.

But the protection caused very annoying warnings, especially for calling
by a string literal which invoke a function like `Post.order("length(title)")`.

Calling by a string literal should be safe since it is not an user
input. So I'd like to allow string literals as a safe raw SQL string.

There is no reliable way to distinguish between `params[:order]` and
`"length(title)"`, but at least `params[:order]` as an user input is
always a mutable string, and `"length(title)"` will be a fstring if
`# frozen_string_literal: true`.

So I think that we can add fstring to the allowlist to mitigate the
annoying warnings without losing the original intention of rails#27947.

Resolves rails#32995.
@firien
Copy link

@firien firien commented May 18, 2020

apologies if I missed something, but was a whitelist added? or was this rolled back?

The following contrived example throws a deprecation warning in active record 5, yet is fine when running active record 6.

require 'bundler'
require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'activerecord', '~> 5'#toggle versions here
  gem 'sqlite3'
end

require 'sqlite3'
require 'active_record'

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :sales do |table|
    table.column :amount, :integer
    table.column :year, :integer
    table.column :name, :string
  end
end

Sale = Class.new(ActiveRecord::Base)

Sale.create(name: 'Dwalin', year: 2019, amount: 12)
Sale.create(name: 'Dwalin', year: 2020, amount: 234)
Sale.create(name: 'Dwalin', year: 2020, amount: 332)
Sale.create(name: 'Gloin', year: 2020, amount: 123)

puts Sale.where(year: 2020).group(:name).having('SUM(sales.amount) > 0').pluck('SUM(sales.amount)')
@jonathanhefner
Copy link
Member

@jonathanhefner jonathanhefner commented May 19, 2020

@firien See #36448.

@SamSaffron
Copy link
Contributor

@SamSaffron SamSaffron commented May 19, 2020

@matthewd / @rafaelfranca what are your thoughts on the fstring bypass proposed by @kamipo , I really like it ❤️ I would certainly kill off our freedom patch if that was in place. fstrings are inherently pretty safe, it is not trivial to get stuff snuck into them?

@henrik
Copy link
Contributor

@henrik henrik commented Sep 2, 2020

This may help others upgrading to Rails 5.2 avoid fixing warnings for queries that Rails 6 will actually allow:

We backported the changed whitelist/allowlist from the above-mentioned #36448 to Rails 5.2, so it no longer warns about stuff like order("RANDOM()") that Rails 6 will allow.

If you want to verify that this backport works, try doing SomeModel.order("RANDOM()").to_sql in a console and check that it no longer complains. And try SomeModel.order("RANDOM() * 2").to_sql and check that it still complains.

Stick this in config/initializers/rails6_backports.rb:

raise "Remove no-longer-needed #{__FILE__}!" if Rails::VERSION::MAJOR >= 6

# Backport https://github.com/rails/rails/pull/36448 to avoid fixing things unnecessarily.
# https://github.com/rails/rails/issues/32995#issuecomment-685475521
module ActiveRecord
  module AttributeMethods
    module ClassMethods
      remove_const(:COLUMN_NAME_WHITELIST)
      COLUMN_NAME_WHITELIST = /
        \A
        (
          (?:
            # table_name.column_name | function(one or no argument)
            ((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
          )
          (?:(?:\s+AS)?\s+\w+)?
        )
        (?:\s*,\s*\g<1>)*
        \z
      /ix

      remove_const(:COLUMN_NAME_ORDER_WHITELIST)
      COLUMN_NAME_ORDER_WHITELIST = /
        \A
        (
          (?:
            # table_name.column_name | function(one or no argument)
            ((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
          )
          (?:\s+ASC|\s+DESC)?
          (?:\s+NULLS\s+(?:FIRST|LAST))?
        )
        (?:\s*,\s*\g<1>)*
        \z
      /ix
    end
  end
end
@searls
Copy link
Contributor Author

@searls searls commented Sep 2, 2020

Great idea, @henrik! The fact so many folks are still stumbling over this as they work in 5.2 has confused me several times as to where Rails 6 actually landed (despite it ultimately not having been a problem at all—upgrade to 6, everybody!)

@jeffdill2
Copy link

@jeffdill2 jeffdill2 commented Oct 19, 2020

If the string param that you pass to order(), for example, doesn't contain basic string interpolation – i.e. #{params[:potentially_unsafe_and_unfiltered_user_input}, can it not be assumed that the raw SQL string is safe from injection?

@andynu
Copy link

@andynu andynu commented Oct 19, 2020

@jeffdill2
Copy link

@jeffdill2 jeffdill2 commented Oct 19, 2020

@andynu derp, of course. Nevermind. 😄

kamipo added a commit to kamipo/rails that referenced this issue Jan 11, 2021
rails#27947 was intended to address `Post.order(params[:order])` which
leading to SQL injection.

But the protection caused very annoying warnings, especially for calling
by a string literal which invoke a function like `Post.order("length(title)")`.

Calling by a string literal should be safe since it is not an user
input. So I'd like to allow string literals as a safe raw SQL string.

There is no reliable way to distinguish between `params[:order]` and
`"length(title)"`, but at least `params[:order]` as an user input is
always a mutable string, and `"length(title)"` will be a fstring if
`# frozen_string_literal: true`.

So I think that we can add fstring to the allowlist to mitigate the
annoying warnings without losing the original intention of rails#27947.

Resolves rails#32995.
kamipo added a commit to kamipo/rails that referenced this issue Jan 12, 2021
rails#27947 was intended to address `Post.order(params[:order])` which
leading to SQL injection.

But the protection caused very annoying warnings, especially for calling
by a string literal which invoke a function like `Post.order("length(title)")`.

Calling by a string literal should be safe since it is not an user
input. So I'd like to allow string literals as a safe raw SQL string.

There is no reliable way to distinguish between `params[:order]` and
`"length(title)"`, but at least `params[:order]` as an user input is
always a mutable string, and `"length(title)"` will be a fstring if
`# frozen_string_literal: true`.

So I think that we can add fstring to the allowlist to mitigate the
annoying warnings without losing the original intention of rails#27947.

Resolves rails#32995.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.