I spoke with Jeremiah Grossman, the Founder and interim CEO of WhiteHat Security, about Ruby on Rails, Django, and the need to add additional time to your estimates for adapting these and other Web frameworks to your environment when developing custom Web applications.
This is supplemental information to CWE-613: Insufficient Session Expiration.
Under Common Consequences:
Scope: Access Control
Effect: Technical Impact: Permanent session hijacking
Under Demonstrative Examples:
The following example is similar to how Ruby on Rails’ CookieStore session storage mechanism works. The entire session object is sent to the Web browser, not just a session identifier linked to a backend session store, meaning that the session credential cannot be revoked.
Under Potential Mitigations/Phase: Implementation:
Ensure expiration time cannot be tampered with when entrusted to the client–In the case of a cookie-based session storage mechanism, ensure the expiration time is absolute (cannot be kept alive) and is contained within the values signed by the application secret token. Additionally, the use of SSL, as well as ‘HttpOnly’ and ‘secure’ cookie attributes, will better protect the session cookie both while in transit and at rest on the client device.
Under Other Notes:
A cookie-based session storage mechanism may prohibit the ability for the user to terminate sessions active with other devices.
The Django Web application framework made to help you build websites fast offers a session storage mechanism that does not allow a visitor to fully terminate their session when they log out. Though not the default storage mechanism — as is the case with Ruby on Rails — it is an option. I found that at least Pinterest and Instagram use this vulnerable option to handle sessions on their websites and I demonstrate what the issue looks like to a normal Web user in my video:
Pinterest.com and Instagram.com both suffer from WASC-47, OWASP Top Ten A2, and CWE-613. The situation is especially bad given the lack of SSL protecting the transport of the permanent session cookie/token between their servers and your browser.
To identify additional high-profile Django-based websites that might use the vulnerable cookie-based session storage mechanism, here is a starting point: http://stackoverflow.com/questions/1906795/what-are-some-famous-websites-built-in-django
Happy hacking! Contact me with questions.
I want to try it out myself you say.
Here is a video explanation using Kickstarter.com as an example:
And here are the steps you take to verify the weakness yourself–using Kickstarter.com, as well as on other websites you suspect are using Rails’ CookieStore (such as those on this list):
- Install a Chrome plugin such as Edit This Cookie to make viewing and editing cookies easier.
- Go to a site such as Kickstarter.com (no SSL!) or one you suspect is using Rails’ CookieStore.
- Find a cookie whose value starts with “BAh7″. That’s a good indicator of Ruby on Rails CookieStore-based websites before version 4.0 of Rails, or those that don’t encrypt their CookieStore values. The session cookie will have a value starting with “BAh7″ then a separator of “–” then a hash digest.
- Open Edit This Cookie using the little icon in the top right of your browser. Find the cookie whose value starts with “BAh7″ and take note of the cookie’s name. In the case of Kickstarter.com it’s “_ksr_session”. Copy the entire cookie data (“BAh7…”).
- Log out of the website.
- Open Edit This Cookie and overwrite the session cookie’s current value (“_ksr_session” in this case) with the data you copied previously.
- Go to the website. You should be in again!
When bringing attention to the session termination security issue present with Ruby on Rails’ CookieStore and Django’s cookie-based session storage mechanism, one of the common questions I get is “Who is using it?”
Well, I did some digging and have the following list of 1,897 websites for your review. These are Rails sites only (before version 4.0, and not including Rails sites that encrypt their cookie values). This is not an exhaustive list, and there is future work to be done in detecting remotely the use of Rails’ CookieStore with encrypted values as well as the presence of Django’s cookie-based storage mechanism.
This Insufficient Session Expiration weakness (WASC-47) is pretty common I found, and it is especially bad when the site does not use SSL. Many of the websites and tools we use store the session hash on the client side, including the applications Redmine, Zendesk, and Spiceworks.
If you don’t have access to the app’s source code, you may be able to figure out if the site you are visiting is using Rails’ CookieStore (before version 4.0 due to its encrypted cookie values) by checking for the string ”BAh7″ at the beginning of the value of any of the cookies. A SHODAN search will reveal tens of thousands of these apps: www.shodanhq.com/search?q=BAh7*
Contact me if you are on the development team of any of the following websites and need help switching.
After bringing attention to the inability to terminate a session in some popular open source web application frameworks, many of the counterarguments fell into the following bins:
- We already knew about this
Why is it still an issue? Too few people know about it; other developers, even users need to be informed and heard from.
- Developers already know about this
They don’t, or they don’t care. They’re busy, rushed, and becoming an expert in your open source project is a lower priority than using it to accomplish whatever they’re being paid to deliver. Burying or omitting shortcomings in your project’s design only delays discovering them–the later: the worse, the angrier.
- Additional configuration is required to fully protect against this
These additional protections are not being deployed. They also don’t provide 100% CYA.
- The issue isn’t sexy
Basic issues are still issues. Basic issues that continue to exist are just embarrassing. Focusing on sexy helps no one.
We’re getting nowhere fast with this attitude.
Separately, there’s disagreement over this issue specifically and if it’s even a vulnerability. Well, the OWASP Top Ten will remain unchanged if we can’t even agree on whether this is a feature or a weakness.
UPDATE: Django updated their documentation to include a warning about this risk: “Unlike other session backends which keep a server-side record of each session and invalidate it when a user logs out, cookie-based sessions are not invalidated when a user logs out. Thus if an attacker steals a user’s cookie, he can use that cookie to login as that user even if the user logs out.”
Django has a session invalidation security vulnerability like the Ruby on Rails vulnerability I wrote about here.
Django is a free and open source Web application framework that is written in Python. Django version 1.4 introduced cookie-based session storage and Django version 1.7 is currently under development.
Django provides options for how and where user session data is stored. One of those options is cookie-based storage, which stores all session data in the cookie and signs it.
Without access to the app’s code, detecting which session storage mechanism is in use is slightly harder than with the similar Rails vulnerability. With Django, the default name for a session cookie is “sessionid” regardless of whether the cookie stores only a session identifier or the complete session data hash. So, you’ll need to examine the value of the cookie to determine which session storage mechanism may be in use for a given application.
When using Django cookie-based session storage in your Web app, if someone were to find, steal, or intercept a user’s cookie they could log into your website as that user even if that user had logged out.
The main difference from the Rails session security vulnerability is that Django does not use cookie-based sessions by default.
I found most developers are completely shocked to learn about this kind of behavior. I believe this is a risk that was written off without adequate documentation or warning.
Happy hacking! Email me with questions: Main@GSMcNamara.com
UPDATE: This issue has received press coverage and made it into the Open Sourced Vulnerability Database (OSVDB) available at http://osvdb.org/show/osvdb/97726. I will follow up on this post with more technical details as well as my research results from studying this issue in the wild.
Ruby on Rails Web applications versions 2.0 through 4.0 are by default vulnerable to an oft-overlooked Web application security issue: Session cookies are valid for life.* The fix is to configure your Rails app to store most session information on the server side in the database.
The default Rails session storage mechanism is the CookieStore, which holds the entire user session hash on the client side in the Web browser as a cookie. In this configuration there is no entry in a “sessions” database table for your Rails app to delete upon logout.
My concern is more than just current session hijacking via Firesheep or similar; a malicious user could use the stolen cookie from any authenticated request by the user to log in as them at any point in the future.
When a user logs out what happens is not what you would expect. Again, no entry in a “sessions” table exists to delete. Instead, Rails will issue a new, empty-ish cookie to the user’s browser in order to overwrite the one granted when the user originally authenticated, and instruct the Web browser to use this newest one from this point forth. This relies on good browser behavior. But remember, the previous cookie is still valid. There is no way to invalidate these old cookies upon sign out with the default Rails configuration. In addition to network snooping (session sidejacking) and XSS, this presents a problem for users accessing your site via a shared or public computer, or perhaps over a faulty network connection that might drop the very last HTTP response requesting that the user’s browser overwrite the stored authenticated cookie. Also, when your users forget to logout, they will not be able to log themselves out of that living session from a different computer, and anyone who discovers the stored cookie can use it indefinitely.
The default cookie name is:
And before Base64 encoding and URL encoding, the cookie value may look something like this with actual values for “[String]”:
While Rails 4 switched to encrypting the value of the cookie, doing so does not eliminate this issue.
Separately, it is a good design for your Web app to require that the user supply their current password before changing sensitive fields such as password or email address. If the CookieStore-stored session were to be hijacked, the malicious user could change the user’s password: 1) immediately invalidating the legitimate user’s cookie and thus slamming your app’s doors in their face and 2) disallowing the legitimate user the ability to log back into their account.
A note about a red herring: if you use the Authlogic gem you may notice a field called “persistence_token” in your users table and believe that you are already using server-side storage for most of your session data. In my testing of the default CookieStore configuration, the field did not appear to serve a purpose.
Switch to ActiveRecordStore or something else from this list. Switching away from CookieStore is said to be slower. After switching, the cookie will contain a value for “session_id” which corresponds to an entry in your database’s sessions table. You will need to keep in mind replicating session data across multiple databases if you have more than one active behind a load balancer.
Happy hacking! Email me with questions: Main@GSMcNamara.com
*In my testing, the only methods to invalidate these cookies are for the user to change their password or for systems administrators to change the application secret. Both are infrequent occurrences.
@GSMcNamara caught a link off twitter a day before it hit F-D, crazy vuln and a good find
— OSVDB (@OSVDB) September 27, 2013
— Threatpost (@threatpost) September 25, 2013
— WhiteHat Security (@whitehatsec) September 27, 2013