How can I make cookies secure (https-only) by default in rails?
Asked Answered
A

7

42

In a Rails controller, I can set a cookie like this:

cookies[:foo] = "bar"

And specify that the "secure" (https-only) flag be on like this:

cookies[:foo, :secure => true] = "bar"

:secure is false by default. How can I have cookies be secure by default, application-wide?

This is on Rails 2.3.8

Averyaveryl answered 22/9, 2010 at 21:1 Comment(0)
A
4

Thanks @knx, you sent me down the right path. Here's the monkeypatch I came up with, which seems to be working:

class ActionController::Response
  def set_cookie_with_security(key, value)
    value = { :value => value } if Hash != value.class
    value[:secure] = true
    set_cookie_without_security(key, value)
  end
  alias_method_chain :set_cookie, :security
end

What do you think?

Averyaveryl answered 23/9, 2010 at 18:9 Comment(5)
John, where in Rails app do we place the class above?Tanberg
great. thank you. one more thing, i my dev env - using webrick - when i set the secure flag to true in environment.rb i cannot get passed my login screen - i noticed there is set cookie from app in responses. any idea what could be going one?Tanberg
if you set the secure flag to true on your cookie, then the cookie won't be sent for non-https requests. are you using https in your dev environment?Averyaveryl
i did set the secure flag to true on my cookie (through ActionController::Base.session). i set up apache httpd with ssl which forwards to WebRick in my dev env and still get same behaviorTanberg
For anyone trying this on Rails 3.2.x, I believe set_cookie has been refactored in to ActionDispatch::Response so monkeypatching that class instead should work.Unipolar
E
53

There's no need to monkeypatch ActionController/ActionDispatch, and force_ssl has side effects (e.g. when behind an ELB).

The most straightforward way to achieve secure cookies is to modify config/initializers/session_store.rb:

MyApp::Application.config.session_store( 
  :cookie_store, 
  key: '_my_app_session',
  secure: Rails.env.production?
)
Euphonic answered 6/7, 2015 at 20:33 Comment(2)
This is the right answer (verified that this works in Rails 4.2.7.1). To test this easily, set secure: true and then you can verify the cookie is generated correctly. Then you can change it back to Rails.env.production?Moffett
it looks like this will only affect the session cookie. The asking person requested secure as the default for all cookies. Can @david-cain verify whether that solution works for all cookies?Herwig
K
20

starting with rails 3.1, according to the rails security guide, you can simply set the following in your application.rb:

config.force_ssl = true

this forces the cookie to be sent over https only (and I assume everything else, too).

Keavy answered 21/9, 2013 at 11:20 Comment(2)
This confused me at first: The main purpose of config.force_ssl is to only allow the rails app to work over SSL, but it also enables the secure flag on cookies. Before you set this config make sure you're okay with the rest of its effects: #15677096Arnettearney
In later 'rails versions config.force_ssl = true is set in production.rb by default in new projects.Staphylorrhaphy
A
4

Thanks @knx, you sent me down the right path. Here's the monkeypatch I came up with, which seems to be working:

class ActionController::Response
  def set_cookie_with_security(key, value)
    value = { :value => value } if Hash != value.class
    value[:secure] = true
    set_cookie_without_security(key, value)
  end
  alias_method_chain :set_cookie, :security
end

What do you think?

Averyaveryl answered 23/9, 2010 at 18:9 Comment(5)
John, where in Rails app do we place the class above?Tanberg
great. thank you. one more thing, i my dev env - using webrick - when i set the secure flag to true in environment.rb i cannot get passed my login screen - i noticed there is set cookie from app in responses. any idea what could be going one?Tanberg
if you set the secure flag to true on your cookie, then the cookie won't be sent for non-https requests. are you using https in your dev environment?Averyaveryl
i did set the secure flag to true on my cookie (through ActionController::Base.session). i set up apache httpd with ssl which forwards to WebRick in my dev env and still get same behaviorTanberg
For anyone trying this on Rails 3.2.x, I believe set_cookie has been refactored in to ActionDispatch::Response so monkeypatching that class instead should work.Unipolar
K
1

Quick and dirty solution: i think it is possible by modifying []= method in action pack cookies module (actionpack/lib/action_controller/cookies.rb)

from:

    def []=(name, options)
      if options.is_a?(Hash)
        options = options.inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
        options["name"] = name.to_s
      else
        options = { "name" => name.to_s, "value" => options }
      end

      set_cookie(options)
    end

to:

    def []=(name, options)
      if options.is_a?(Hash)
        options.merge!({:secure => true})
        options = options.inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
        options["name"] = name.to_s
      else
        options = { "name" => name.to_s, "value" => options }
      end

      set_cookie(options)
    end
Kerwinn answered 23/9, 2010 at 0:28 Comment(0)
G
1
# session only available over HTTPS
ActionController::Base.session_options[:secure] = true
Godard answered 20/11, 2013 at 4:1 Comment(6)
is this a new feature in rails 4?Averyaveryl
I dont know. However, in Rails 4 it works. You can also check this property in YOUR version of Rails.Godard
This is for the session cookie only, right? Not for cookies in general?Banksia
It doesn't work in Rails 4.1 : undefined method 'session_options' for ActionController::Base:Class (NoMethodError)Bey
Where to put this?Mirador
apidock.com/rails/ActionController/SessionManagement/… This method is deprecated or moved on the latest stable version. The last existing version (v2.3.8) is shown here.Maddening
D
0

You should look at the rack-ssl-enforcer gem. I was just looking for a clean answer to this and it solves the problem independent of which version of Rails you're on, plus it's extremely configurable.

Deduct answered 17/6, 2016 at 20:42 Comment(1)
Or the secure_cookies gem.Vaenfila
P
0

You can do this as mentioned in some of the above answers (use secure option in the config/initializers/session_store.rb file):

MyApp::Application.config.session_store :cookie_store, key: '_my_app_session',
                                                       secure: Rails.env.production?

which will only secure the session cookie, but other cookies will not be secure.

If you want to secure all the cookies in your Rails app by default, you can use the secure_headers gem. Just add the secure_headers gem to your Gemfile, bundle install the gem and create a config/initializers/secure_headers.rb file with this content:

SecureHeaders::Configuration.default do |config|
  config.cookies = {
    secure: true, # mark all cookies as "Secure"
  }
end

This will make all the cookies secure in your Rails app by default.

You can also add these recommended configurations and set the httponly and samesite options as well:

SecureHeaders::Configuration.default do |config|
  config.cookies = {
    secure: true, # mark all cookies as "Secure"
    httponly: true, # mark all cookies as "HttpOnly"
    samesite: {
      lax: true # mark all cookies as SameSite=lax
    }
  }
end
Punjab answered 7/8, 2020 at 5:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.