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

Configurable token randomness #67

Merged
merged 4 commits into from
Aug 3, 2017

Conversation

mtsmfm
Copy link
Contributor

@mtsmfm mtsmfm commented Jun 13, 2017

To increase security, I want to change token randomness by configuration 🔑

@Ch4s3
Copy link
Contributor

Ch4s3 commented Jun 13, 2017

Should we set a minimum?

@mtsmfm
Copy link
Contributor Author

mtsmfm commented Jun 15, 2017

@Ch4s3 good to have but I'm not sure how long minimum should be 🤔

@@ -12,7 +12,7 @@ def self.included(base)

# Random code, used for salt and temporary tokens.
def self.generate_random_token
SecureRandom.urlsafe_base64(15).tr('lIO0', 'sxyz')
SecureRandom.urlsafe_base64(Controller::Config.token_randomness).tr('lIO0', 'sxyz')

Choose a reason for hiding this comment

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

i think this is not safety enough.
can you consider to do something like below?

SecureRandom.urlsafe_base64([15, Controller::Config.token_randomness].max).tr('lIO0', 'sxyz')

Choose a reason for hiding this comment

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

and there may be one more thing, you can calculate real length of the result value if you want.

real_length = (15 * 3) / 4
SecureRandom.urlsafe_base64(real_length).tr('lIO0', 'sxyz').size
=> 15

Copy link
Contributor

Choose a reason for hiding this comment

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

[15, Controller::Config.token_randomness].max

It is weird that it isn't set the specified value by user. So, I think the specified value should always be set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

and there may be one more thing, you can calculate real length of the result value if you want.

I think it is difficult to control real length:

[3] pry(main)> (15..100).select {|i| SecureRandom.urlsafe_base64((i * 3) / 4).size != i }
=> [17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i think this is not safety enough.
can you consider to do something like below?

I agree with setting minimum randomness as 15 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We (@kyuden and I) talked in person and we think the specified value always should be respected so I won't implement minimum value.

@@ -12,6 +12,8 @@ class << self
attr_accessor :save_return_to_url
# set domain option for cookies
attr_accessor :cookie_domain
# Set token randomness
attr_accessor :token_randomness
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorcery:Controler::Config is configuration used for Sorcery::Controller. But token_randomness is used for Sorcery::Model::TemporaryToken So, It is better to set it with Sorcery:Model::Config like encryption_algorithm

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can't access Sorcery::Model::Config instance directly because Sorcery::Model::TemporaryToken is a helper module and isn't included into user_class.

If you really want to do that, the code will be the following:

Sorcery::Controller::Config.user_class.to_s.constantize.sorcery_config.token_randomness

https://github.com/Sorcery/sorcery/blob/36698d7/lib/sorcery/adapters/active_record_adapter.rb#L22

Should I keep the setting in Sorcery:Controler::Config or do such trick?

Copy link
Contributor Author

@mtsmfm mtsmfm Jun 16, 2017

Choose a reason for hiding this comment

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

💭 IMO, it seems that Sorcery::Model::Config is singleton so it is not bad to change implementation to access the instance as same as Sorcery::Controller::Config

Sorcery::Model::Config.token_randomness #=> 15

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TemporaryToken is under Model module so I'll move this setting to Sorcery::Model::Config

@mtsmfm mtsmfm force-pushed the config-for-token-randomness branch from 36698d7 to 19143fc Compare June 18, 2017 06:43
@mtsmfm
Copy link
Contributor Author

mtsmfm commented Jun 18, 2017

@kyuden @akinrt Could you review this PR again? 🙏

# The length of the result string is about 4/3 of `token_randomness`.
# Default: `15`
#
# config.token_randomness =
Copy link
Contributor

Choose a reason for hiding this comment

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

s/config.token_randomness =/user.token_randomness =/

@mtsmfm Could you actually check that not only the test code but also this setting works?

Copy link
Contributor Author

@mtsmfm mtsmfm Jun 18, 2017

Choose a reason for hiding this comment

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

@kyuden

Fixed 🙇


I confirmed that a token in password reset email becomes longer:

screen shot 2017-06-18 at 20 37 45

screen shot 2017-06-18 at 20 38 28

@mtsmfm mtsmfm force-pushed the config-for-token-randomness branch from 19143fc to d0e403a Compare June 18, 2017 11:36
@kyuden
Copy link
Contributor

kyuden commented Jun 18, 2017

@athix I approved this PR. Could you take a look at this?

@kyuden
Copy link
Contributor

kyuden commented Jun 24, 2017

@athix Are you busy in this week?

@joshbuker
Copy link
Member

@kyuden Sorry, yeah I've been busy with some personal stuff and work. I'll try to get a review for this done as soon as I can.

@kyuden
Copy link
Contributor

kyuden commented Jun 27, 2017

@athix thx!

Copy link
Member

@joshbuker joshbuker left a comment

Choose a reason for hiding this comment

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

Looks good, but I'd like to see if we can make the sorcery_config call consistent with other similar calls before rolling this out.

@@ -12,7 +12,7 @@ def self.included(base)

# Random code, used for salt and temporary tokens.
def self.generate_random_token
SecureRandom.urlsafe_base64(15).tr('lIO0', 'sxyz')
SecureRandom.urlsafe_base64(Sorcery::Controller::Config.user_class.to_s.constantize.sorcery_config.token_randomness).tr('lIO0', 'sxyz')
Copy link
Member

Choose a reason for hiding this comment

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

It looks like this randomization method is used globally throughout Sorcery, so I'm a bit hesitant tying the config specifically to the user. Also, it seems a bit odd that we'd have to hook through the Controller config to get the value, instead of being able to call sorcery_config directly. Looks like getting that to work isn't exactly trivial though because the generate method is outside of ClassMethods/InstanceMethods.

I'll keep digging around and see if I can figure out what's going on in there.

@@ -0,0 +1,27 @@
require 'spec_helper'
Copy link
Member

Choose a reason for hiding this comment

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

Yay! Thanks for writing tests for the new functionality. 👍

@@ -7,12 +7,14 @@ module Model
# such as reseting password and activating the user by email.
module TemporaryToken
def self.included(base)
# FIXME: This may not be the ideal way of passing sorcery_config to generate_random_token.
@sorcery_config = base.sorcery_config
Copy link
Member

Choose a reason for hiding this comment

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

Mildly worried that this might cause @sorcery_config to be unintentionally exposed, but not familiar enough with how ruby inheritance/privacy works to be sure. This is how it gets saved in lib/sorcery/model.rb however.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sorcery_config is owned by TemporaryToken.
It will be overridden if we include TemporaryToken.

module Foo
  def self.included(base)
    @var = base.name
  end

  def self.foo
    puts @var
  end
end

class Bar
  include Foo
end

Foo.foo #=> Bar
puts Foo.instance_variable_get(:@var) #=> Bar

class Baz
  include Foo
end

Foo.foo #=> Baz
puts Foo.instance_variable_get(:@var) #=> Baz

Is this intentional behaviour?

Copy link
Member

Choose a reason for hiding this comment

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

As far as I can tell, Sorcery::Model is the only place where TemporaryToken is/should be included. Based on your description, it sounds like it should be working exactly as intended. What I was worried about was more along the lines of @sorcery_config accidentally becoming available inside of a controller/model class.

For example:

class User < ApplicationRecord
  authenticates_with_sorcery!

  def some_method
    # @sorcery_config should not be available here!
    @sorcery_config = nil # This hopefully shouldn't break things either!
  end
end

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for late reply 💦

Don't worry, User instance can't override @sorcery_config

module Foo
  def self.included(base)
    @var = base.name
  end

  def self.foo
    @var
  end
end

class Bar
  include Foo

  def bar
    @var
  end

  def bar=(var)
    @var = var
  end
end

p Foo.foo #=> "Bar"
p Foo.instance_variable_get(:@var) #=> "Bar"
bar = Bar.new
p bar.bar #=> nil
bar.bar = :bar
p bar.bar #=> :bar
p Foo.foo #=> Bar
p Foo.instance_variable_get(:@var) #=> Bar

@joshbuker
Copy link
Member

@Ch4s3 I think this is probably ready to go now, mind giving this a once over?

@joshbuker
Copy link
Member

I'm planning on cutting a new release sometime in the next couple weeks, if I don't hear any objections before then I'll merge this in and include it for that release.

@joshbuker joshbuker merged commit c9b8108 into Sorcery:master Aug 3, 2017
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.

5 participants