I’d like to announce that Devise v2.2.3, v2.1.3, v2.0.5 and v1.5.4 have been released with a security patch. Upgrade immediately unless you are using PostgreSQL or SQLite3. Users of all other databases (including NoSQL ones) require immediate upgrade.
Using a specially crafted request, an attacker could trick the database type conversion code to return incorrect records. For some token values this could allow an attacker to bypass the proper checks and gain control of other accounts.
In case you are using a Devise series older than the ones listed above, recommendations are provided below back to v1.2 series. Regardless, an upgrade to more recent versions is advised.
We checked all Devise versions released in the previous two years and recommendations follows as below.
v1.5, v2.0, v2.1 and v2.2 series
You can upgrade to any of v2.2.3, v2.1.3, v2.0.5 and v1.5.4. In case an upgrade is not feasible, please add the following patch to
config/initializers/devise_patch.rb inside your Rails application:
Devise::ParamFilter.class_eval do def param_requires_string_conversion?(_value); true; end end
Please add the following patch to
config/initializers/devise_patch.rb inside your Rails application:
Devise::Models::Authenticatable::ClassMethods.class_eval do def auth_param_requires_string_conversion?(value); true; end end
Please upgrade to more recent versions.
v1.2 and v1.3 series
Not affected by this vulnerability. Please upgrade to more recent versions.
When upgrading to any of v2.2.3, v2.1.3, v2.0.5 and v1.5.4, some people may be relying on some wrong behaviour to filter data retrieved on authentication. For example, one may have writen in his model:
def find_for_authentication(conditions) conditions[:active] = true super end
The code above may no longer work and needs to be rewriten as:
def find_for_authentication(conditions) find_first_by_auth_conditions(conditions, active: true) end
Thank you notes
We would like to thank joernchen of Phenoelit for disclosing this vulnerability and working with us on a patch.
After 4 months of our last major version release, we’re releasing Devise 2.1.0, which includes several bug fixes, some new features and the removal of features deprecated on Devise 2.0. If you’re eager to do the update, please check Devise’s wiki page about Upgrading from 2.0 to 2.1. You can also check the changelog or the commits between 2.0.4 and 2.1.0.
Encrytable is now a gem
As it was only used for old encryption algorithms like sha1 or md5, we have extracted encryptable module to a separated ruby gem. So, if you’re using the encryptable module, you should only require it on your Gemfile and you’re good to go!
To allow a developer to cherry-pick which features they want to add to their models, Devise splits its behaviors into modules. One of the consequences of such splitting is that you don’t know if the persistence layer provides all fields required by the behavior. For example, the database authenticatable module requires a
encrypted_password field. If the field does not exist, you will end up getting an error during a request. Usually those fields are automatically added to the migration when you call the devise generator, but if you decide to include a module after, you can easily forget to add the new fields.
That said, in order to provide faster feedback, Devise now has a method that checks if a given class is missing any of the required fields and raises an error if so. You can call this method as follow (in case your Devise class is
We’ve implemented this feature in an agnostic way, to not depend on a specific ORM, but only to verify if the instance responds to the required fields. So even if your ORM does everything through
method_missing, you should be able to use this method (we’re relying that you also have implemented a working
respond_to?, which is strongly recommendeded when using
check_fields! is called, Devise will detect the included modules in the given class and, if there is a missing attribute, it will raise a
Devise::Models::MissingAttribute exception with a message telling you all required fields that doesn’t exist. You can easily use that method with your favorite test framework:
And then you will be able to check if your migrations have added the correct fields to your database.
A message to module maintainers
If you’re a maintainer of a Devise module, you should add a method to each of your modules called
self.required_fields(klass) that returns an array of required fields. If the method is absent, you will get a deprecation warning.
UPDATE: Fixed a class name and corrected a grammar error.
We have just released Devise 2.0. This version is not a big refactoring, nor contains stellar features, it is simply another step towards a very mature authentication library.
Over the years, we have learned from our own experience and from your feedback, how to better use Devise. We’ve made changes to improve security and also to make the library more flexible. All those changes were backwards compatible, forcing us to maintain different branches of logic in the same source code to accomodate those different scenarios. Devise 2.0 simply deprecates those old scenarios so we can clean up the code.
When you migrate to 2.0, you should get some warnings. The amount of warnings will depend on when you started using Devise. For recent applications, it should take 2 minutes to update (basically fixing up migrations and the locale file), older applications should be up to speed in 15-30 minutes. We have created a page with instructions to upgrade, be sure to read it for a smoother experience.
Finally, Devise 2.0 also includes:
- Support to the reconfirmable feature. When on, if the user changes his e-mail, he needs to confirm the new e-mail address before the e-mail is finally updated;
- We got rid of
t.database_authenticatableand friends in migrations in favor of explicit code;
- Better support to engines. For example, we now support
config.parent_controllerwhich you can set to something different from
ApplicationController. There is a good sample engine here;
- Rails 3.2 support. The previous Devise version (v1.5) runs fine but with a few deprecation warnings, v2.0 gets rid of them;
Thanks everyone that contributed to this awesome release and, if you have any issues, please let us know in the issues tracker. Here to a better future!
We, Rails developers, have always worried about improving the performance of our test suites. Today I would like to share three quick tips we employ in our projects that can drastically speed up your test suite.
1. Reduce Devise.stretches
Add the following to your spec/test helper:
Devise.stretches = 1
Explanation: Devise uses bcrypt-ruby by default to encrypt your password. Bcrypt is one of the best choices for such job because, different from other hash libraries like MD5, SHA1, SHA2, it was designed to be slow. So if someone steals your database it will take a long time for them to crack each password in it.
That said, it is expected that Devise will also be slow during tests as many tests are generating and comparing passwords. For this reason, a very easy way to improve your test suite performance is to reduce the value in
Devise.stretches, which represents the cost taken while generating a password with bcrypt. This will make your passwords less secure, but that is ok as long as it applies only to the test environment.
Latest Devise versions already set stretches to one on test environments in your initializer, but if you have an older application, this will yield a nice improvement!
2. Increase your log level
Add the following to your spec/test helper:
Rails.logger.level = 4
Explanation: Rails by default logs everything that is happening in your test environment to “log/test.log”. By increasing the logger level, you will be able to reduce the IO during your tests. The only downside of this approach is that, if a test is failing, you won’t have anything logged. In such cases, just comment the configuration option above and run your tests again.
3. Use shared connection with transactional fixtures
class ActiveRecord::Base mattr_accessor :shared_connection @@shared_connection = nil def self.connection @@shared_connection || retrieve_connection end end # Forces all threads to share the same connection. This works on # Capybara because it starts the web server in a thread. ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
Explanation: A long time ago, when Rails was still in 1.x branch, a new configuration option called
use_transactional_fixtures was added to Rails. This feature is very simple: before each test Active Record will issue a begin transaction statement and issue a rollback after the test is executed. This is awesome because Active Record will ensure that no data will be left in our database by simply using transactions, which is really, really fast.
use_transactional_fixtures and use Database Cleaner to clean up their database after each test. However, this affects your test suite performance badly.
The patch above, however, provides a very simple solution to both problems. It forces Active Record to share the same connection between all threads. This is not a problem in your test suite because when the test thread is running, there is no request in the server thread. When the server thread is running, the test thread is waiting for a response from the server. So it is unlikely that both will use the connection at the same time. Therefore, with the patch above, you no longer need to use Database Cleaner (unless you are using another database like Mongo) and, more importantly, you must turn
use_transactional_fixtures back to true, which will create a transaction wrapping both your test and server data, providing a great boost in your test suite performance.
Finally, if any part of your code is using threads to access the database and you need to test it, you can just set
ActiveRecord::Base.shared_connection = nil during that specific test and everything should work great!
That’s it! I hope you have enjoyed those tips and, if they helped you boost your test suite performance, please let us know in the comments the time your test suite took to run before and after those changes! Also, please share any tips you may have as well!
When talking about Devise, I’ve mentioned that, before creating Devise, we were alternating between using Authlogic or Clearance in our projects. However, we soon realized that we needed a solution that was as customizable as Authlogic (allowing us to choose behaviors and several configuration options) and as complete as Clearance (whole MVC stack). It is fun to remember this happened more than 2 years ago.
After the presentation, someone came to talk to me about Sorcery and said it would be nice if Devise provided the same kind of tooling, allowing someone to build their own controllers and views around Devise instead of using Devise built-in controllers and views. His proposal surprised me, because this approach is totally possible with Devise and it was one of our design goals since day one.
That said, we realized that we were probably not “advertising” the bare-bone, stripped-down aspect of Devise well enough. That’s why I am writing this blog post. Devise already makes it easy for you to customize your own views, using the generator
rails g devise:views which copies the views to your application. But what if you want to roll out your own views AND controllers?
To show you how we can achieve that, let’s write some code! The first step is to create a Rails application:
rails new devise-only-model
Next, we will add Devise to the Gemfile:
gem "devise", "~> 1.4.6"
And run the installation generator:
bundle install && rails g devise:install
The installation generator is going to give you some extra instructions, so don’t forget to do that as well. Next, let’s generate our basic User model, but we will pass an extra parameter called
rails g devise User --skip-routes
By passing this extra parameter, Devise is going to generate everything as usual, with a small difference on
devise_for :users, :skip => :all
This parameter tells Devise to not generate any route at all. You can check that by executing
bundle exec rake routes. However, you may be wondering: why can’t we simply remove the
devise_for call? If we remove the route, Devise wouldn’t actually know that you have added Devise configuration to the User model, as all models are lazy loaded. So we need the route to tell Devise it needs to setup the appropriate helpers for the user, like
With Devise configured, we are ready to create the controllers and views on our own. In this blog post, we are going to create the
SessionsController as an example allowing us to sign in and sign out. First, let’s add our routes:
root :to => "sessions#new" post "/users/sign_in" => "sessions#create" delete "/users/sign_out" => "sessions#destroy"
app/controllers/sessions_controller looks like:
class SessionsController < ApplicationController # For security purposes, Devise just authenticates an user # from the params hash if we explicitly allow it to. That's # why we need to call the before filter below. before_filter :allow_params_authentication!, :only => :create def new @user = User.new(params[:user]) end def create # Since the authentication happens in the rack layer, # we need to tell Devise to call the action "sessions#new" # in case something goes bad. Feel free to change it. user = authenticate_user!(:recall => "sessions#new") flash[:notice] = "You are now signed in!" sign_in user redirect_to root_path end def destroy sign_out flash[:notice] = "You are now signed out!" redirect_to root_path end end
The controller implementation is quite straightforward. With the controller in hands, we just need to generate the view for the new action at
<% if user_signed_in? %> You are signed in as <%= current_user.email %>. <%= link_to "Sign out", users_sign_out_path, :method => :delete %>. <% else %> <%= form_for @user, :url => users_sign_in_path do |f| %> <div><%= f.label :email %><br /> <%= f.email_field :email %></div> <div><%= f.label :password %><br /> <%= f.password_field :password %></div> <div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div> <div><%= f.submit "Sign in" %></div> <% end %> <% end %>
The view shows a message if the user is signed in, otherwise it shows a sign in form. Now we are almost ready to check if it works. First, we need to run the migrations:
bundle exec rake db:migrate
Remove the index page:
And create a user in the database so we can sign in. We can do that in
User.create!(:email => "email@example.com", :password => "123456")
Now, start the server and you are ready to sign in and sign out. You can also block user access in any controller by calling
authenticate_user! in a before filter. Just remember that, if you add the filter to your application controller, remember to skip the filter on the sessions controller, otherwise you won’t be able to sign in in the first place.
You can now freely proceed to implement the other controllers and views in your application. Keep in mind that if you have
devise :recoverable in your model, all the related methods like
User.send_reset_password_instructions will already be available in your model, so you can use them straight away to implement your own reset password feature. Since Devise use all those methods internally, if you have any questions about implementing your own reset password feature, you can always take a look at Devise own controllers for some help.
I hope this post can help you to roll out your own controllers if this is the kind of feature you expect from Devise. Also, if you are worried about the overhead of using Devise even if you are not using its controllers, there is no need to worry at all. Devise does the smart thing and only loads the controllers you are actually using. Also, it lazily loads all behaviors, so if you are not using recoverable, no code related to recoverable will be loaded at all.
It is important to keep in mind that Devise was built by us to be flexible and capable of handling different requirements from different clients, so it is PlataformaTec’s priority to have it as flexible as possible! So, what would you like to see in “bare-bone, stripped-down Devise” in order to better use it in your applications?
Note: Devise 1.1.6 broke compatibility with Rails versions prior to 3.0.4, this has been fixed on Devise 1.1.7.
Devise 1.1.6 has just been released and it follows Rails 3.0.4 release. Rails 3.0.4 changes how CSRF works and adds a new method called
handle_unverified_request that should be properly overridden by authentication frameworks. Devise 1.1.6 implements this method and others small security fixes.
If you have updated to Rails 3.0.4, you must update to Devise 1.1.6. Those using Devise 1.2.rc should use master for awhile, another RC is coming soon. For more information, check out the CHANGELOG.
Devise 1.0.10 was also released with the same fixes for Rails 2.3.11.