1. Introduction
Web application frameworks assist developers in building web applications, including some aspects of security. However, there’s no one-size-fits-all security solution, as it relies on the users of the framework, the development method, and an understanding of all layers of the web application environment. Staying up-to-date on all layers and knowing potential threats are vital for developing secure applications. In this article, we’ll focus on session and CSRF and explore common security problems and countermeasures that help us create secure web applications.
2. Session
This chapter describes some particular attacks related to sessions and security measures to protect your session data.
2.1 What are Sessions?
Sessions refer to a temporary storage mechanism used in web development to maintain user information on the server during a specific browsing session. They help keep track of user activity across different website pages and end once the browser is closed.
2.2 Session Hijacking
Stealing a user’s session ID allows an attacker to impersonate the victim on a web application. When a user logs in, the application stores their user ID in a session hash, and the corresponding session ID in a cookie server as temporary authentication anyone seizing this cookie can access the application as that user.
There are several ways to hijack a session, such as sniffing the cookie on an insecure network, using a public terminal where cookies were not cleared, through a cross-site scripting (XSS) exploit, or fixing a known session identifier. Countermeasures include using secure connections over SSL, providing a prominent log-out button, and other specific practices to prevent these vulnerabilities.
In Rails 3.1 and later, it’s possible to force SSL connection in the application config file:
config.force_ssl = true
2.3 Session Storage
Rails uses ActionDispatch::Session::CookieStore
as the default session storage, saving the session hash on the client-side, and the temporary nature of cookies, which may lead to malicious reuse. To mitigate security risks, Rails encrypts cookies, providing integrity and confidentiality. The encryption and verification keys are derived from the secret_key_base configuration value, which must be long and random. To maintain key strength, different salt values must be used for encrypted and signed cookies. In test and development, a secret_key_base is derived from the app name, while other environments use a random key. If the application’s secrets are exposed, changing them, especially secret_key_base, is strongly recommended.
Decrypted state:
# config/credentials.yml.enc
secret_key_base: 492f...
2.4 Rotating Encrypted and Signed Cookies Configurations
Rotating encrypted and signed cookie configurations is a process that allows old cookies to be gradually updated without immediate invalidation. This gives users the opportunity to have their cookies rewritten with new configurations, such as changing the digest used for signed cookies.
For example, to switch from SHA1 to SHA256 for signed cookies, you would:
- Assign the new digest:
Rails.application.config.action_dispatch.signed_cookie_digest = "SHA256"
- Add a rotation for the old SHA1 digest to ensure old cookies are upgraded:
Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
cookies.rotate :signed, digest: "SHA1"
end
With this configuration, new cookies will be digested with SHA256, while old SHA1 cookies can still be read and rewritten with the new digest. Once the upgrade is complete, the rotation can be removed.
The process allows flexibility with key rotation, though having many rotations simultaneously is uncommon.
2.5 Replay Attacks for CookieStore Sessions
A replay attack is a potential vulnerability when CookieStore. Here’s how it can be exploited:
- A user receives credits, and the amount is stored in a session.
- The user makes a purchase, adjusting the credit value in the session.
- The user replaces the current cookie with a previously copied cookie from the first step, restoring their original credit.
A possible solution is to include a nonce (a one-time random value) in the session, but managing nonces across multiple servers can be complex and may defeat the purpose of using CookieStore.
The best defense against replay attacks in this context is to avoid strong sensitive data like credits in the session. Instead, store the credits in the database and only keep information like the logged_in_user_id in the session. This approach minimizes the risk and avoids the complexities of handling nonces.
2.6 Sessioon Fixation
Session fixation is an attack where an attacker predefines a session ID to gain unauthorized access to a user’s account or data.
The process involves:
- An attacker obtains a valid session ID by loading the login page of the targeted web application.
- The attacker keeps the session alive by accessing the application periodically.
- The attacker injects JavaScript (via XSS) into the targeted application to force the user’s browser to adopt the fixed session ID.
Here is an example script:
<script>document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";</script>
- The victim is lured to the infected page, and the browser adopts the trap session ID.
- Since the trap session is unused, the application prompts the user to authenticate.
- Both the victim and the attacker now co-use the web application with the same session, making it valid without the victim noticing the attack.
2.7 Session Fixation – Countermeasures
The most effective countermeasure against session fixation is to issue a new session identifier and invalidate the old one after a successful login. In Rails, this can be done simply with the command reset_session
. If using the Devise gem for user management, sessions will automatically expire on sign-in and sign-out. Additional measures can include saving user-specific properties in the session, such as the remote IP address or user agent, and verifying them with each request, although this approach may have limitations with users behind proxies.
2.8 Session Expiry
Sessions that never expire can be vulnerable to attacks like CSRF, session hijacking, and session fixation. One way to mitigate this risk is by setting an expiry time for the sessions.
You can expire sessions on the server side, which is safer than relying on client-side cookies. In a database table, you can call a method like Session.sweep(20.minutes)
to expire sessions that are older than 20 minutes. Here’s an example.
class Session < ApplicationRecord
def self.sweep(time = 1.hour)
where(updated_at: ...time.ago).delete_all
end
end
To deal with attackers who might keep a session alive, you can also add a created_at
column to the sessions table and delete sessions that were created a long time ago:
where(updated_at: ...time.ago).or(where(created_at: ...2.days.ago)).delete_all
This approach ensures that sessions are regularly cleaned up and minimizes the window of opportunity for potential attacks.
3. Cross-Site Request Forgery (CSRF)
Cross-Site Request Forgery (CSRF) is an attack that tricks the victim’s browser into executing an unwanted action on a web application in which the user is authenticated. The attack works by including malicious code or a link that accesses a web application where the user has an active session.
Here’s an example of how it might work:
- A hacker crafts an HTML image element pointing to a command in the user’s application, like
<img src="http:www.webapp.com/project/1/desttroy">
. - The victim, Bob, views the post containing the image while still logged into the web application.
- His browser tries to load the “image”, sending along the cookie with the valid session ID.
- The web application verifies the session ID and executes the unauthorized command, e.g., destroying project number one.
The malicious code or link doesn’t have to be in the web application’s domain; it can be anywhere linked in a forum, blog, post, or email. CSRF is considered a significant security risk, although it appears rarely in common vulnerability databases. It’s often referred to as a “sleeping giant” in terms of web security.
Conclusion
We explored session and CSRF with Ruby on Rails example. Rails has many ways to easily run secure apps. I’ll continue learning security with Rails, and writing articles about it. Learn the basics of security and create a secure app!