Storing an encrypted cookie with Rails - ruby-on-rails

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.

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.

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!

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.

Related

how to get the persistent cookie value and session cookie value in rails application

Im developing ruby on rails application. I want to get the persistent cookie value and session cookie value in the application. Can any please guide me on this
I have read that request.session_options[:id] will fetch the session_id, is that the one that is usually stored in session cookie? Please guide me if my way of understanding is wrong.Thanks in advance
In Rails, it is simple as calling the session or cookies hash.
# Set a simple session cookie
cookies[:user_name] = "david"
# Read a cookie
cookies[:user_name] # => "david"
# Delete a key
cookies.delete :user_name
The same goes for session.
So, the information that you are looking for is probably inside one of these two.
Take a look at the examples at http://www.tutorialspoint.com/ruby-on-rails/rails-session-cookies.htm

My cookie token is strong enough in order to use that for user authentication purposes?

I am running Ruby on Rails 3 and I would know if the code that I am using in order to set the cookie value for user authentication purposes is strong enough.
In my model I have:
require 'digest'
class User < ActiveRecord::Base
...
def make_cookie_id_salt(string)
secure_hash("#{self.id}--#{string}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
In my controller I have:
cookies.signed[:current_user_id] = { :value => [#user.id, #user.make_cookie_id_salt(#user.id)], :expires => 15.days.from_now }
Is it strong enough? If no, how I can improve that (make an example!)?
Everything that gets put into cookies is stored as plain text.
If you set a cookie, and then check the cookies in your browser you will notice (in your case the cookie name would be current_user_id) that it is represented by a string of characters like: G8gcm9sbCB5b3VyIG93biBhdXRoIHRvIGt... (Not quite plain text, right? It is actually Base64 encoded, but you can easily read it - require('base64'); Base64.decode64(string)).
Rails stores a special _yourapp_session cookie that is used to check the cookies validity. If for example someone/something was trying to modify it, it would get rejected.
Now in your case it doesn't really matter if you try to hash something in the cookie or not.
It is just used for authentication (to look up a user in the database by his id) and you are not storing any unique secret data (Which you should not place in a cookie anyway, but it would be the only reason to hash something)
Of course someone could steal the cookie of a user (if he used a public computer and hasn't cleared his cache, etc.) and log in, but there's no way to prevent that (No matter what kind of hashing was used to obfsucate it)
In conclusion you should be fine with what you have.
Rather than try to create your own, I suggest using the Authlogic gem. In a few minutes of configuration you get a complete authentication solution, including cookies and much more. If you really want to roll your own, install the Authlogic gem and take a look at how they do it.
Devise is another option. It's extremely configurable, pretty DRY, with exhausting wiki.
For now-days I prefer it over Authlogic.

How to deal with authentication for a Ruby API wrapper?

I'm working on an API wrapper for Viddler, which will eventually be made public, and I'm trying to figure out the best way to deal with authentication/API keys, specifically with usage within Rails applications in mind.
The easiest way to write the wrapper would be to just have the code create a new client each time, and the developer could store the API key in a constant for future use:
#client = Viddler::Client.new(VIDDLER_API_KEY)
The problem with this is, it's kind of clunky to have to keep creating client objects and passing in the API key. This gets even more complicated when you throw user authentication into the mix.
I'm thinking some sort of solution where I all the the API key to be set in the environment file and then the authentication would be done in a before_filter.
Viddler::Client.api_key = 'abc123'
Viddler::Client.authenticate! 'username', 'password'
Viddler::Client would then store this in a class variable, and you could call Viddler::Client.new without any parameters and make authenticated calls. One thing I'd be concerned about is that this means the developer would have to be sure to clear out the authentication before or after each request, since the class variables would persist between requests.
Any thoughts?
Storing the API key globally would for sure be pretty useful and certainly is the way to go for that kind of information. User authentication on the other hand I think shouldn't be stored globally, never ever, especially for a high level API, because telling your users to "ensure to add an after_filter :reset_viddler_auth" might lead to some unexpected security risks.
# in a config/initializer/*.rb file or something
Viddler::Client.api_key = "abc123"
# in the controller/action/model/wherever
#client = Viddler::Client.new # anonymous
#client.authenticate!("username", "password") # authenticate anon client
#client_auth = Viddler::Client.new("username", "password") # authenticated client
Guess like that you've best of both worlds :) Maybe even provide a way to create a new client with another API key like,
#client_other = Viddler::Client.new("username", "password", :api_key => "xyz890")
So... just my 2 cents.
PS: not sure how up-to-date it is, but there's already a ruby viddler wrapper, just FYI, http://viddler.rubyforge.org/rdoc/

Can't understand sessions in Rails

Please don't bit my for my misunderstanding.
The sessions are very new for me, and i have some problems.
Okay i read many information about sessions and especially rails session. But this don't give me right imagine about sessions.
Did i understand right, when users send request to server (get) -> Server create a new session (and store this some file in hard drive with session id), session id -> is a random generated num? so, server create a new session (and store session on drive) after this server send back answer to client and set session_id in cookies?
Ok, i debug some params and see some results:
debug(session):
{:_csrf_token=>"jeONIfNxFmnpDn/xt6I0icNK1m3EB3CzT9KMntNk7KU=", :session_id=>"06c5628155efaa6446582c491499af6d", "flash"=>{}}
debug(cookies):
{"remember_user_token"=>"1::3GFRFyXb83lffzwPDPQd", "_blog_session"=>"BAh7CDoQX2NzcmZfdG9rZW4iMWplT05JZk54Rm1ucERuL3h0NkkwaWNOSzFtM0VCM0N6VDlLTW50Tms3S1U9Og9zZXNzaW9uX2lkIiUwNmM1NjI4MTU1ZWZhYTY0NDY1ODJjNDkxNDk5YWY2ZCIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA==--348c88b594e98f4bf6389d94383134fbe9b03095"}
Okay, i know, what _csrf_token helps to prevent csrf.
session_id -> is id of the session which stored on hard drive (by default)
but what is _blog_session in cookies?
also, remeber_user_token containes my id (1::*) and what about second part, what is it?
Sorry for this stupid questions, i know what i can easy use any nice auth-plugins (authlogic/clearance/devise), but i want to fully understand sessions.
Thank you.
(also sorry for my english, this is not my native language)
remember_user_token is probably set by your authentication plugin, it is encrypted string, which is stored in users table and is used to authenticate him. Details can vary between plugins.
Second part: you are probably using cookie based session store (it is default),
So, _blog_session stores your encrypted session data.
More about cookie based sessions here and here.
The name "_blog_session" is set in config/initializers/session_store.rb
It looks like:
# Your secret key for verifying cookie session data integrity.
# If you change this key, all old sessions will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
ActionController::Base.session = {
:key => '_blogs_session',
:secret => '07fb6f0d41af4ae06aebb1696fcbb5a5398d4a08570744a4cd53ff237020c43a2022b4041d617d95bcf3f5c4601c7e6c1646eecfc157cc200e7dfedd7d7c6813'
}

Accessing the "session key" cookie name from anywhere in Rails

We are building a plugin for Rails to be used within iframe Facebook applications, and at one point we need to check if Rail's session id cookie as been set or not.
By default, this cookie is named _myprojectname_session, what we need to find out is the actual name of the cookie itself. So if it's not set, we can do some redirects to make sure the cookies are set.
How do we access the damn name of the cookie from anywhere? Or at least from within a controller?
Rails.application.config.session_options[:key]
I found the solution. In Rails 2.3.2 at least the session key in set in config/initializers/session_store.rb like this:
ActionController::Base.session = {
:key => '_myapp_session',
:secret => '[...]'
}
And you can read the value like this:
ActionController::Base.session_options[:key]
From Base.session to Base.session_options automagically, doesn't make much sense, and it caused me a big headache... lol
To access the name of the session cookie from within the view or the controller, you can say:
request.session_options[:session_key]
and then to access the raw value of that cookie, being an empty array if it's not set, you use:
request.cookies[ request.session_options[:session_key] ]
The cookie name ( aka session_key ) is set in your config/environment.rb file.
config.action_controller.session = {
:session_key => '_project_session',
:secret => 'long-secret-key'
}
In my experience, if there is an underscore in the key, IE SOMETIMES does not set the cookies. In other words, use 'projectsession' instead of '_project_session'.
I think that the session key is stored in a variable called ENV_SESSION_KEY
Note also this bug which affects tests around session_options in some versions of Rails 2.x: https://rails.lighthouseapp.com/projects/8994/tickets/2303-testrequest-doesnt-initialize-session_options
In Rails 3/4 I'm using this:
request.env["rack.request.cookie_hash"]
This is a hash that contains all the cookies for current user including the one for session.
The hash has as a key the name of the cookie, and as a value the value of the cookie
I couldn't work out how to do this in rails 3 :-(
Eventually I ended up putting this in config/initializers/session_store.rb
SESSION_KEY = '_myapp_session'
MyApp::Application.config.session_store :cookie_store, :key => SESSION_KEY
and then accessing this where needed, eg in a view...
<%= ::ENV_SESSION_KEY %>

Resources