We are glad to announce that Devise 3.1.0.rc is out. On this version, we have focused on some security enhancements regarding our defaults and the deprecation of
TokenAuthenticatable. This blog post explains the rationale behind those changes and how to upgrade.
Devise 3.1.0.rc runs on both Rails 3.2 and Rails 4.0. There is a TL;DR for upgrading at the end of this post.
Note: We have yanked 3.1.0.rc and released to 3.1.0.rc2 which fixes some regressions. Thanks everyone for trying out the release candidates!
Do not sign the user in after confirmation
In previous Devise versions, the user was automatically signed in after confirmation. This meant that anyone that could access the confirmation e-mail could sign into someone’s account by simply clicking the link.
Automatically signing the user in could also be harmful in the e-mail reconfirmation workflow. Imagine that a user decides to change his e-mail address and, while doing so, he makes a typo on the new e-mail address. An e-mail will be sent to another address which, with the token in hands, would be able to sign in into that account.
If the user corrects the e-mail straight away, no harm will be done. But if not, someone else could sign into that account and the user would not know that it happened.
For this reason, Devise 3.1 no longer signs the user automatically in after confirmation. You can temporarily bring the old behavior back after upgrading by setting the following in your
config.allow_insecure_sign_in_after_confirmation = true
This option will be available only temporarily to aid migration.
Thanks to Andri Möll for reporting this issue.
Do not confirm account after password reset
In previous Devise versions, resetting the password automatically confirmed user accounts. This worked fine in previous Devise versions which confirmed the e-mail just on sign up, so the e-mail both confirmation and password reset tokens would be sent to were guaranteed to be the same. With the addition of reconfirmable, this setup change and Devise will no longer confirm the account after password reset.
Thanks to Andri Möll for reporting this issue and working with us on a fix.
CSRF on sign in
Devise’s sign in page was vulnerable to CSRF attacks when used with the rememberable feature. Note that the CSRF vulnerability is restricted only to the sign in page, allowing an attacker to sign the user in an account controlled by the attacker. This vulnerability does not allow the attacker to access or change a user account in any way.
This issue is fixed on Devise 3.1.0 as well as 3.0.2 and 2.2.6. Users on previous Devise versions can patch their application by simply defining the following in their
Devise.mappings.each_key do |key|
Thanks to Kevin Dew for reporting this issue and working with us on a fix.
Store digested tokens in the database
In previous versions, Devise stored the tokens for confirmation, reset password and unlock directly in the database. This meant that somebody with read access to the database could use such tokens to sign in as someone else by, for example, resetting their password.
In Devise 3.1, we store an encrypted token in the database and the actual token is sent only via e-mail to the user. This means that:
- Devise now requires a
config.secret_keyconfiguration. As soon as you boot your application under Devise 3.1, you will get an error with information about how to proceed;
- Every time the user asks a token to be resent, a new token will be generated;
- The Devise mailer now receives one extra token argument on each method. If you have customized the Devise mailer, you will have to update it. All mailers views also need to be updated to use
@token, as shown here, instead of getting the token directly from the resource;
- Any previously stored token in the database will no longer work unless you set
config.allow_insecure_token_lookup = truein your Devise initializer. We recomend users upgrading to set this option on production only for a couple days, allowing users that just requested a token to get their job done.
Thanks to Stephen Touset for reporting this issue and working with us on a solution.
Jay Feldblum also wrote to us to let us know that our tokens lookup are also vulnerable to timing attacks. Although we haven’t heard of any exploit via timing attacks on database tokens, there is a lot of research happening in this area and some attacks have been successful over the local network. For this reason, we have decided to protect applications using Devise from now on.
By digesting the confirmation, reset password and unlock tokens, as described in the previous section, we automatically protected those tokens from timing attacks.
However, we cannot digest the authentication token provided by
TokenAuthenticatable, as they are often part of APIs where the token is used many times. Since the usage of the authenticatable token can vary considerably in between applications, each requiring different safety guarantees, we have decided to remove
TokenAuthenticatable from Devise, allowing users to pick the best option. This gist describes two of the available solutions.
Thanks to Jay Feldblum for reporting this issue and working with us on a solution.
TL;DR for upgrading
As soon as you update Devise, you will get a warning asking you to set your
config.secret_key. By upgrading Devise, your previous confirmation, reset and unlock tokens in the database will no longer work unless you set the following option to true in your Devise initializer:
config.allow_insecure_token_lookup = true
It is recommended to leave this option on just for a couple days, just to allow recently generated tokens by your application to be consumed by users.
TokenAuthenticable has not been affected by those changes, however it has been deprecated and you will have to move to your own token authentication mechanisms.
Furthermore, the Devise mailer now receives an extra token argument on each method. If you have customized the Devise mailer, you will have to update it. All mailers views also need to be updated to use
@token, as shown here, instead of getting the token directly from the resource.
With those changes, we hope to provide an even more secure authentication solution for Rails developers, while maintaining the flexibility expected from Devise.