In a Rails app, the session cookie can be easily set to include the secure
cookie attribute, when sending over HTTPS to ensure that the cookie is not leaked over a
The same problem happened to me, and I solved it this way: In session_store.rb I configured:
MyApp::Application.config.session_store :cache_store, key: COOKIE_NAME, :expire_after => 1.days, :expires_in => 1.days, :domain => 'mydomain.com', same_site: :none
(note that I didn't put the , same_site: :none
here, because it would kill the set cookie completely when serving HTTP)
And then I monkey patched the Rack->Utils->set_cookie_header, by placing this file in the initializers
folder
require 'rack/utils'
module Rack
module Utils
def self.set_cookie_header!(header, key, value)
case value
when Hash
domain = "; domain=" + value[:domain] if value[:domain]
path = "; path=" + value[:path] if value[:path]
max_age = "; max-age=" + value[:max_age] if value[:max_age]
expires = "; expires=" +
rfc2822(value[:expires].clone.gmtime) if value[:expires]
# Make secure always, even in HTTP
# secure = "; secure" if value[:secure]
secure = "; secure"
httponly = "; HttpOnly" if value[:httponly]
same_site =
case value[:same_site]
when false, nil
nil
when :none, 'None', :None
'; SameSite=None'
when :lax, 'Lax', :Lax
'; SameSite=Lax'
when true, :strict, 'Strict', :Strict
'; SameSite=Strict'
else
raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
end
value = value[:value]
end
value = [value] unless Array === value
cookie = escape(key) + "=" +
value.map { |v| escape v }.join("&") +
"#{domain}#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}"
case header["Set-Cookie"]
when nil, ''
header["Set-Cookie"] = cookie
when String
header["Set-Cookie"] = [header["Set-Cookie"], cookie].join("\n")
when Array
header["Set-Cookie"] = (header["Set-Cookie"] + [cookie]).join("\n")
end
nil
end
end
end
The base issue is, that by definition of Set-Cookie, cookies with secure set may only be sent via secure connetions.
So not sending cookies with secure set over HTTP is the expected behavior.
You might want to set different cookie options in different environments. In config/environments/developent.rb you clould set
Rails.application.configure do
config.session_store :cache_store, key: COOKIE_NAME, same_site: :none
end
and in production (config/environments/production.rb), where you deploy your site with HTTPS:
Rails.application.configure do
config.session_store :cache_store, key: COOKIE_NAME, same_site: :lax, secure: true
end
Secure cookies are not sent over non-secure connections by definition.
Terminating SSL upstream is quite common, but you need to pass certain header fields through so that Rails knows and can do the right thing.
Here's a document that explains the configuration in pretty good detail for nginx. Search for "Set headers" to jump to the section describing the specific headers you need to pass through.
There are security considerations using this configuration, e.g., if the device terminating SSL is not on the same secure LAN as the Rails host, then you have a vulnerability.