Session-only cookie corruption in Ruby web apps

Rack and Rails have a cookie monster.

Browsers place limits on the number and size of cookies present for a domain or in a response. If you exceed these limits, Bad Things can happen. Rack and Rails try to prevent this in the obvious cases, but this post describes what they get wrong in their current implementations. We’ll also review the potential impact of—and how you can mitigate—this type of issue in your Ruby web apps.

This information is most relevant for web apps that transmit session cookies that contain the encoded contents of the entire session hash—not just the session ID. In other words, a cookie-only session. For example, Rack::Session::Cookie with Marshal or JSON, Rails’ default ActionDispatch::Session::CookieStore, or an implementation of JWT (JSON Web Tokens) that uses a cookie (instead of a dedicated response header) as its transport. The security risks are greatest when cookie-only sessions meet the cookie-truncation behavior of older browsers (and can be compounded when the sessions contain arbitrarily-large data, such as flash messages).

TL;DR: If your Ruby web apps use cookie-only sessions, consider adding Rack::Protection::MaximumCookie to their middleware stacks.

Continue reading

Always set X-Forwarded-Proto

Aside

If your Rack application (and probably other types of applications) lives behind a reverse proxy, always set an X-Forwarded-Proto request header. I recently ran into an issue where Rack::Session (which I’m using for an OAuth1.0a server-side login flow) wouldn’t let me set a secure cookie because it didn’t think I was serving a secure web site.

I have forwardfor disabled in my HAproxy config because I’m using PROXY protocol to talk to Varnish. Manually adding the X-Forwarded-Proto header to the request fixed the issue, and now I want those three hours of my life back! Hopefully this comes up in someone else’s frantic googling at some point in the future and saves them a similar headache.