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

active_record: Type cast booleans and durations for string columns. #16069

Merged
merged 1 commit into from
Jul 6, 2014

Conversation

dylanahsmith
Copy link
Contributor

@rafaelfranca, @sgrif, @arthurnn this is a basically a rebase of pull #14894 which was closed without the problem being completely fixed.

Problem

Pull #16037 partially fixed the problem is described in Potential Query Manipulation with Common Rails Practises by quoting Numeric types, however, that pull request missing the need to quote boolean and ActiveSupport::Duration types.

For example:

User.where(:login_token=>params[:token]).first

can be exploited by passing in false for params[:token] through a JSON body causing the query

SELECT * FROM `users` WHERE `login_token` = 0 LIMIT 1;

to match any login_token that starts with a non-digit character.

Quoting for these additional types was properly handled in mistakenly closed pull request #14894.

Solution

ActiveRecord::Type::String#type_cast_for_database has been updated to handle these types. Tests are included for these missed types as well as the numeric types which are already quoted properly to prevent regressions.

when ::String then ::String.new(value)
when true then "1"
when false then "0"
else super
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we just to_s everything at this point? Or whitelist the things that we shouldn't?

AFAICS, something along those lines is necessary in order to honour type_cast_for_database's contract: we're obliged to accept any value, but only permitted to return an instance of one of a few classes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want that to be what happens, but its not possible yet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally the contract is that we only accept values that type_cast_from_user or type_cast_from_database return. Method like where and update_all bypass normal type casting at the moment.

The reason this can't to_s everything yet is because of some weirdness in serialized attributes which I plan on looking into today.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I think where is the only method that is "safe" to pass user input to which doesn't go through type_cast_from_user)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, it would definitely be safer if we could to_s by default.

@matthewd
Copy link
Member

matthewd commented Jul 6, 2014

... or is the problem that we're not going through any type_cast_from_* when we take this avenue? 😕


def test_where_with_integer_for_binary_column
count = Binary.where(:data => 0).count
assert_equal 0, count
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is failing on mysql: https://travis-ci.org/rails/rails/jobs/29233923#L192 (but not mysql2)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I initially added the .to_s before unpack was called in ActiveRecord::Type::Binary::Data#hex so that I could test binary columns, but updated this pull request to call .to_s in the ActiveRecord::Type::Binary::Data constructor instead to fix this vulnerability that this test uncovered in the mysql adapter. I think the difference is that the mysql adapter uses type casting without quoting, so was skipping the hex method which was called for quoting.

matthewd added a commit that referenced this pull request Jul 6, 2014
active_record: Type cast booleans and durations for string columns.
@matthewd matthewd merged commit b974033 into rails:master Jul 6, 2014
@matthewd
Copy link
Member

matthewd commented Jul 6, 2014

Thanks for checking & rebasing this! 💛

@sgrif I'll leave you to opine on and, if necessary, adjust exactly where the casting happens... but these tests sure sound like things we want passing.

@sgrif
Copy link
Contributor

sgrif commented Jul 6, 2014

I'm concerned that this sets a precedent for what we cast in type_cast_for_database. Longer term I'd like to see where do through a full from user for database cycle. Short term this seems like the best solution, thanks for updating this and pinging me!

@sgrif
Copy link
Contributor

sgrif commented Jul 6, 2014

"Oh, you're trying to look up a string column by an interger? I should probably cast the column. Casting the lookup value sounds like too much work, let's just compare everything to 0 instead. :trollface:" Scumbag mysql...

@dylanahsmith
Copy link
Contributor Author

Thanks for the quick review and merge!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants