Storing an encrypted cookie with Rails
Asked Answered
U

3

9

I need to store a small piece of data (less than 10 characters) in a cookie in Rails and I need it to be secure. I don't want anybody being able to read that piece of data or injecting their own piece of data (as that would open up the app to many kinds of attacks). I think encrypting the contents of the cookie is the way to go (should I also sign it?). What is the best way to do it?

Right now I'm doing this, which looks secure, but many things looked secure to people that knew much more than I about security and then it was discovered it wasn't really secure.

I'm saving the secret in this way:

encryptor = ActiveSupport::MessageEncryptor.new(Example::Application.config.secret_token)
cookies[:secret] = {
  :value => encryptor.encrypt(secret),
  :domain => "example.com",
  :secure => !(Rails.env.test? || Rails.env.development?)
}

and then I'm reading it like this:

encryptor = ActiveSupport::MessageEncryptor.new(Example::Application.config.secret_token)
secret = encryptor.decrypt(cookies[:secret])

Is that secure? Any better ways of doing it?

Update: I know about Rails' session and how it is secure, both by signing the cookie and by optionally storing the contents of the session server side and I do use the session for what it is for. But my question here is about storing a cookie, a piece of information I do not want in the session but I still need it to be secure.

Unhelm answered 20/2, 2012 at 16:49 Comment(4)
Out of interest, why does it need to be stored in a cookie rather than server-side, where it would be by definition more secure?Cords
@russell well, you need to store something in the browser, right? otherwise you wouldn't be able to identify it again. Whether it's the actual data or an id of a record on a table it's a matter of preference and necessity regarding the size of the data and other considerations. What I'm storing is already the id of a record in the table. I could create another table, but it seems like a waste in this case.Unhelm
I've added an answer because I think changing the default session store to use the DB will give you the security you want with very little overhead with regards to creating tables etc.Cords
@Cords I do not want to use the session. My sessions are per domain (blah.example.com) while this piece of data is for the whole system (.example.com), hence the domain option when setting it.Unhelm
U
1

I'm re-posting JacobM's answer, that he deleted, because it was the correct answer and pointed me in the right direction. If he undeletes it, I'll delete this one and pick his as the best answer.

First of all, if you use encrypt_and_verify instead of encrypt it will sign the cookie for you.

However, when it comes to security, I always prefer to rely on solutions that have been vetted in public, rather than rolling my own. An example would be the encrypted-cookies gem.

Unhelm answered 21/2, 2012 at 10:3 Comment(2)
I curious why you would not use the signed cookies?(supported by default in Rails 3.x)Flighty
@KandadaBoggu The cookies processed by encrypt_and_verify or the encrypted-cookies gem (post 1.0) are signed, but also encrypted. It wouldn't be that serious if this piece of data gets exposed, but I rather not think about it and just keep it secure by being encrypted, thus, opaque to the user.Unhelm
F
13
  • Setting a secure cookie

    cookies.signed[:secret] = {
     :value => "foo bar",
     :domain => "example.com",
     :secure => !(Rails.env.test? || Rails.env.development?)
    }
    
  • Accessing the cookie

    cookies.signed[:secret] # returns "foo bar"
    

The cookie is signed using ActionController::Base.cookie_verifier_secret. You can set the cookie_verifier_secret in the initializer file.

Flighty answered 20/2, 2012 at 18:11 Comment(3)
I do not want to use the session. My sessions are per domain (blah.example.com) while this piece of data is for the whole system (.example.com), hence the domain option when setting it.Unhelm
Updated the answer take a look.Flighty
Cookies set with .signed need to be accessed with .signed too. So your second code block should be cookies.signed[:secret].Ennis
C
1

As KandadaBoggu says, it looks like what you want is a session variable, and session variables are by default encrypted and stored in cookies. However, if you have a look at the contents of config/initializers/session_store.rb you will find something like the following:

 # Be sure to restart your server when you modify this file.
 MyRailsApp::Application.config.session_store :cookie_store, :key => '_my_rails_app_session'

 # Use the database for sessions instead of the cookie-based default,
 # which shouldn't be used to store highly confidential information
 # (create the session table with "rails generate session_migration")
 # MyRailsApp::Application.config.session_store :active_record_store

Which suggests to me that you should use the database for sessions instead of the cookie-based default, which shouldn't be used to store highly confidential information. The pre-cooked migration makes everything really easy to set up so there's very little overhead in doing so, and once it's done there's basically zero overhead if you need to add a new piece of secret information at a later date!

Cords answered 20/2, 2012 at 23:32 Comment(1)
I do not want to use the session. My sessions are per domain (blah.example.com) while this piece of data is for the whole system (.example.com), hence the domain option when setting it.Unhelm
U
1

I'm re-posting JacobM's answer, that he deleted, because it was the correct answer and pointed me in the right direction. If he undeletes it, I'll delete this one and pick his as the best answer.

First of all, if you use encrypt_and_verify instead of encrypt it will sign the cookie for you.

However, when it comes to security, I always prefer to rely on solutions that have been vetted in public, rather than rolling my own. An example would be the encrypted-cookies gem.

Unhelm answered 21/2, 2012 at 10:3 Comment(2)
I curious why you would not use the signed cookies?(supported by default in Rails 3.x)Flighty
@KandadaBoggu The cookies processed by encrypt_and_verify or the encrypted-cookies gem (post 1.0) are signed, but also encrypted. It wouldn't be that serious if this piece of data gets exposed, but I rather not think about it and just keep it secure by being encrypted, thus, opaque to the user.Unhelm

© 2022 - 2024 — McMap. All rights reserved.