Sensitive data stored in client-side storage - Ruby

Sensitive data stored in client-side storage - Ruby

Need

Secure storage of sensitive data on the client-side

Context

  • Usage of Ruby 2.0+ for developing Ruby applications
  • Usage of Ruby on Rails for building web applications

Description

Non compliant code

        # In a controller action
def store
  cookies[:user_email] = params[:email]
  cookies[:user_password] = params[:password]
end
        
        

In the above code, we are storing user's sensitive information (email and password) in the client-side storage (cookies). This is a part of a Ruby on Rails controller action.

This is done using the cookies method provided by Rails, which allows you to read and write data to the client's cookies. The line cookies[:user_email] = params[:email] is storing the user's email in a cookie, and the line cookies[:user_password] = params[:password] is storing the user's password in a cookie.

This is a vulnerability because cookies are stored in the client's browser and can be accessed by anyone who has access to the client's machine. If an attacker gets access to these cookies, they can read the user's email and password, potentially leading to unauthorized access to the user's account.

Furthermore, if the application is not using secure and HttpOnly flags with cookies, it also makes the cookies vulnerable to attacks like Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF).

Steps

  • Avoid storing sensitive information in client-side storage.
  • Instead, store sensitive information on the server-side.
  • Use secure methods to transmit sensitive information between the client and server, such as HTTPS.
  • Implement proper authentication and authorization mechanisms to protect sensitive data.
  • Encrypt sensitive data before storing it on the server-side.
  • Implement secure session management to handle user authentication and session data.
  • Regularly review and update security practices to stay up-to-date with the latest vulnerabilities and best practices.

Compliant code

        # In a controller action
def store
  user = User.find_by(email: params[:email])
  if user && user.authenticate(params[:password])
    session[:user_id] = user.id
  else
    # Handle login failure
  end
end
        
        

The updated code no longer stores sensitive user information (email and password) in cookies, which are client-side storage. Instead, it uses the server-side session storage to store the user's ID after successful authentication.

Here's a step-by-step breakdown of the updated code:

1. The User.find_by(email: params[:email]) line attempts to find a user in the database with the provided email.

2. If a user with the provided email is found, user.authenticate(params[:password]) checks if the provided password is correct.

3. If the authentication is successful, session[:user_id] = user.id stores the user's ID in the server-side session storage. This ID can be used to identify the user in subsequent requests.

4. If the authentication fails, the else block handles the login failure. This could involve rendering an error message to the user, redirecting to the login page, etc. This part is left as a comment because the exact implementation will depend on the specifics of your application.

This approach ensures that sensitive user information is not exposed in client-side storage, reducing the risk of unauthorized read operations. It also leverages the built-in authentication mechanisms of Ruby on Rails, which are designed to be secure and efficient.

Remember to always use HTTPS to protect data in transit between the client and server, and to implement proper authorization checks to ensure that only authenticated users can access sensitive data.

References