How to get cookie expiration time in Rails? - ruby-on-rails

I readed here:
Get cookie expiration
that only name and value are sent to the server so no other cookie data is available.
That means that there is no way to get the expiration date of the cookie?
So i have to save that date in my database, if i need it later?

That's right, only cookie-name and cookie-value are returned.
This is not a shortcoming of Rails or PHP, it is defined this way in RFC 6265:
Notice that the cookie attributes are not returned. In particular,
the server cannot determine from the Cookie header alone when a
cookie will expire, for which hosts the cookie is valid, for which
paths the cookie is valid, or whether the cookie was set with the
Secure or HttpOnly attributes.

While the accepted answer is right, I came here because I wanted to make sure that my application sets a permanent cookie (with a expiration date in the far future) and not a normal one.
If you want to verify that this is the case (and you do not care about the exact expiration date), you can set an expectation like this (the example uses the Mocha gem):
ActionDispatch::Cookies::PermanentCookieJar.any_instance.expects(:[]=).with(:key, "value").once
This expectation will pass with exactly one call of cookies.permanent[:key] = "value" but will fail for cookies[:key] = "value".
It also works for signed cookies (cookies.permanent.signed[:key] = "value"). However, note that a signed cookie will have its value encrypted based on your application's secret_key_base, so you will have to adjust the expectation to something like
ActionDispatch::Cookies::PermanentCookieJar.any_instance.expects(:[]=).with(:key, anything).once
instead.

Related

How to set cookie with some delay or find how old is cookie in ruby

I set a cookie returning_user
cookies[:returning_user] = { value: "anonymus" , expires: (Time.current + 1.hours) }
Now I want to perform some action if the cookie is 10min older, so is there any way to find out how old a cookie is.
If no, then how can I set that cookie after 10mins.like in Javascript I can use setTimeout for that
setTimeout(()=>{
.....setcookielogic will come here...
},10*60*1000);
I am new to ruby, hope someone can help me in this.
When cookies are sent in an HTTP header, their expiration time is NOT sent with them, just the value. However, if you want to know when the cookie is set, put the creation time in the cookie (or another cookie). So maybe something like:
> cookies[:returning_user_cookie_set_time] = {value: Time.current + 1.hours, expires: (Time.current + 1.hours)}
This makes sense since you are trying to maintain state with a piece of data...kind of what cookies are for. As opposed to trying to maintain state by manipulating the cookie syntax.
Of course if you are using rails, you can just keep the data in the session and achieve the same thing without risking someone seeing/manipulating your backend data.

Can you tamper with the cookie expiration date of a signed cookie in Rails?

As far as I'm aware, once a cookie is sent as a response (and the cookie includes an expiration date), the expiration date is not sent back with the request, just the cookie content.
Thus, even if a cookie is signed in Rails, is it possible to just tamper with its expiration date and set it way far in the future than it actually is?
Yes, it is possible to only tamper with a cookie's expiration date for an encrypted/signed cookie, unless you provide additional security.
Seeing how the value is encrypted anyway, you could always add additional information to the cookie value - eg. the expiration date (and store the entire value in an array form, with element index 0 being the value and element index 1 being the cookie expiration date, for instance, or use some query within that string and split the string to get the value). Then, in your controller, parse the cookie value into an array and check the value of expiration date (eg if it should've already expired, in that case raise an error) and set the cookie's expiration date back to the correct, non-tampered value for the client. That way, every time you require a value of that cookie, you can always check whether the cookie should still even be present or whether the expiration date had been tampered with.
I should think you needn't do this often and the application of it is for a quite specific controller method. It's a hackish way, sure, but it's the only way to be really sure both the cookie value and the cookie expiration date hadn't been tampered with.
yes, you surely can.
cookies.signed[:ssid] = { value: '', expires: Time.now + 30.minutes } OR
cookies.signed[:ssid] = { value: '', expires: Time.now + 60.years }
Here value can be a string or an array.
With this you can set the cookie expiry as far in the future you want.

Why is rails constantly sending back a Set-Cookie header?

I'm experiencing issues with an elastic load balancer and varnish cache with respect to cookies and sessions getting mixed up between rails and the client. Part of the problem is, rails is adding a "Set-Cookie" header on with a session id on almost every request. If the client already is sending session_id, and it matches the session_id that rails is going to set.. why would rails continuously tell clients "oh yeah.. you're session id is ..."
Summary: Set-Cookie headers are set on almost every response, because
the default session store will try to write the session data to an encrypted cookie on any request that has accessed the session (either to read from it or write to it),
the encrypted value changes even when the plain text value hasn't,
the encryption happens before it reaches the code that's responsible for checking if a cookie value has changed to avoid redundant Set-Cookie headers.
Plain-text cookies
In Rails, the ActionDispatch::Cookies middleware is responsible for writing Set-Cookie response headers based on the contents of a ActionDispatch::Cookies::CookieJar.
The normal behaviour is what you'd expect: if a cookie's value hasn't changed from what was in the request's Cookie header, and the expiry date isn't being updated, then Rails won't send a new Set-Cookie header in the response.
This is taken care of by a conditional in CookieJar#[]= which compares the value already stored in the cookie jar against the new value that's being written.
Encrypted cookies
To handle encrypted cookies, Rails provides an ActionDispatch::Cookies::EncryptedCookieJar class.
The EncryptedCookieJar relies on ActiveSupport::MessageEncryptor to provide the encryption and decryption, which uses a random initialisation vector every time it's called. This means it's almost guaranteed to return a different encrypted string even when it's given the same plain text string. In other words, if I decrypt my session data, and then re-encrypt it, I'll end up with a different string to the one I started with.
The EncryptedCookieJar doesn't do very much: it wraps a regular CookieJar, and just provides encryption as data goes in, and decryption as data comes back out. This means that the CookieJar#[]= method is still responsible for checking if a cookie's value has changed, and it doesn't even know the value it's been given is encrypted.
These two properties of the EncryptedCookieJar explain why setting an encrypted cookie without changing its value will always result in a Set-Cookie header.
The session store
Rails provides different session stores. Most of them store the session data on a server (e.g. in memcached), but the default— ActionDispatch::Session::CookieStore—uses EncryptedCookieJar to store all of the data in an encrypted cookie.
ActionDispatch::Session::CookieStore inherits a #commit_session? method from Rack::Session::Abstract::Persisted, which determines if the cookie should be set. If the session's been loaded, then the answer is pretty much always “yes, set the cookie”.
As we've already seen, in the cases where the session's been loaded but not changed we're still going to end up with a different encrypted value, and therefore a Set-Cookie header.
See the answer by #georgebrock on why this happens. It's pretty easy to patch rails to change this behaviour to only set the cookie if the session changes. Just drop this code in the initializers directory.
require 'rack/session/abstract/id' # defeat autoloading
module ActionDispatch
class Request
class Session # :nodoc:
def changed?;#changed;end
def load_for_write!
load! unless loaded?
#changed = true
end
end
end
end
module Rack
module Session
module Abstract
class Persisted
private
def commit_session?(req, session, options)
if options[:skip]
false
else
has_session = session.changed? || forced_session_update?(session, options)
has_session && security_matches?(req, options)
end
end
end
end
end
end

Passing a token securely from one controller method to another

I want to generate a random token for my user. For UX reasons, I want to generate this token in MyController#new in order to show it in new view. Then I need to pass it to create method. However, I want to prevent user from changing it.
I know of 3 ways to do it:
pass it as hidden field
pass it through sessions
write it to database, marked as incomplete, then set it to complete in create method
The first two approaches are not secure, while the last is overkill.
Is there a way to securely pass a parameter from new to create method in controller?
Sessions in Rails and # and above are very very secure because they are hashed. The official library says there are no practically evidences of session hash been compromised so the user can only delete that token from the browser but they can't change it to a logically correct value.
What you can do is save the value in sessions and make a hash value yourself from that token and save it also in session then on the receiving side you can regenerate the hash from the session and verify the value.
no user can edit both the session values that they match. If its not matching you can discard the values and throw to an error page.
I hope i answered you, if there is any misunderstanding pls ask.

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'
}

Resources