Concurrent sessions - Scala

Concurrent sessions - Scala

Need

Enforce session management and limit concurrent user sessions

Context

  • Usage of Scala for building scalable and high-performance applications
  • Usage of play.api.mvc for handling HTTP requests and responses in Play Framework

Description

Non compliant code

        import play.api.mvc._

class ApplicationController extends Controller {
  def login = Action { request =>
    val username = request.body.asFormUrlEncoded.get("username").head
    val password = request.body.asFormUrlEncoded.get("password").head

    // Check if the username and password are valid
    if (validate(username, password)) {
      val sessionId = createSession(username)
      Ok("Logged in").withSession("session" -> sessionId)
    } else {
      Unauthorized("Invalid username or password")
    }
  }

  def logout = Action { request =>
    request.session.get("session").map { session =>
      deleteSession(session)
      Ok("Logged out").withNewSession
    }.getOrElse {
      Unauthorized("No active session")
    }
  }

  private def validate(username: String, password: String): Boolean = {
    // Validate the username and password
    true
  }

  private def createSession(username: String): String = {
    // Create a new session for the user
    "sessionId"
  }

  private def deleteSession(session: String): Unit = {
    // Delete the user's session
  }
}
        
        

The above code represents a basic login and logout functionality in a Scala backend application using the Play framework.

In login function, it accepts a username and password from the request body, validates them, and if they are valid, it creates a new session for the user and returns a successful response with the session ID.

In logout function, it retrieves the session ID from the request, deletes the corresponding session, and returns a successful response without any session data.

The vulnerability here is that the application does not validate the number of active sessions each user has. This means that a user can login more than once at the same time from different locations, creating multiple active sessions. The application also does not notify the user when a session has already been initiated nor when a second one commences from a different location. This can lead to potential unauthorized access and misuse of the application.

Steps

  • Implement session management to track the number of active sessions per user.
  • When a user logs in, check if they already have an active session and handle accordingly.
  • If a user tries to initiate a new session while already having an active session, notify them and provide an option to terminate the existing session.
  • If a user tries to initiate a new session from a different location while already having an active session, notify them and provide an option to terminate the existing session.
  • Ensure that the session management is secure and cannot be easily tampered with or hijacked.
  • Consider implementing additional security measures such as session expiration and session invalidation on logout.

Compliant code

        import play.api.mvc._

class ApplicationController extends Controller {
  private val activeSessions = collection.mutable.Map[String, String]()

  def login = Action { request =>
    val username = request.body.asFormUrlEncoded.get("username").head
    val password = request.body.asFormUrlEncoded.get("password").head

    // Check if the username and password are valid
    if (validate(username, password)) {
      activeSessions.get(username) match {
        case Some(sessionId) =>
          Conflict("User already logged in. Please logout from other sessions.")
        case None =>
          val sessionId = createSession(username)
          activeSessions += (username -> sessionId)
          Ok("Logged in").withSession("session" -> sessionId)
      }
    } else {
      Unauthorized("Invalid username or password")
    }
  }

  def logout = Action { request =>
    request.session.get("session").map { session =>
      activeSessions.find(_._2 == session).map { case (username, _) =>
        deleteSession(session)
        activeSessions -= username
        Ok("Logged out").withNewSession
      }.getOrElse {
        Unauthorized("No active session")
      }
    }.getOrElse {
      Unauthorized("No active session")
    }
  }

  private def validate(username: String, password: String): Boolean = {
    // Validate the username and password
    true
  }

  private def createSession(username: String): String = {
    // Create a new session for the user
    "sessionId"
  }

  private def deleteSession(session: String): Unit = {
    // Delete the user's session
  }
}
        
        

The updated code now includes a mutable Map activeSessions to keep track of active sessions for each user.

When a user tries to log in, the application first checks if the username and password are valid. If they are, it then checks if there is already an active session for that user in the activeSessions map. If there is, it returns a Conflict response indicating that the user is already logged in and needs to log out from other sessions. If there isn't, it creates a new session, adds it to the activeSessions map, and returns a successful response.

When a user tries to log out, the application first checks if there is an active session for that user. If there is, it finds the corresponding username in the activeSessions map, deletes the session, removes the entry from the activeSessions map, and returns a successful response. If there isn't, it returns an Unauthorized response indicating that there is no active session.

This way, the application ensures that each user can only have one active session at a time, and that users are notified when they try to initiate a new session while already having an active session.

References