Concurrent sessions control bypass - Scala

Concurrent sessions control bypass - Scala

Need

Enforcement of concurrent session control

Context

  • Usage of Scala for functional and object-oriented programming in the JVM
  • Usage of play.api.mvc for handling HTTP requests and responses in Play Framework

Description

Non compliant code

        import play.api.mvc._

object Application extends Controller {
  def index = Action { request =>
    request.session.get("connected").map { user =>
      Ok("You are connected as " + user)
    }.getOrElse {
      Unauthorized("You are not connected")
    }
  }

  def authenticate = Action { request =>
    request.session.get("connected").map { user =>
      Ok("You are already connected as " + user)
    }.getOrElse {
      Ok("You are now connected").withSession("connected" -> "user")
    }
  }
}
        
        

The vulnerable code above represents a simple authentication system using the Play framework in Scala. The index method checks if there is a session associated with the user. If there is, it returns a message saying the user is connected. If not, it returns an unauthorized error.

The authenticate method is where the vulnerability lies. It checks if there is a session associated with the user. If there is, it returns a message saying the user is already connected. If not, it starts a new session and returns a message that the user is now connected.

The vulnerability here is that the authenticate method does not handle the case where a user already has a session started and tries to start a new one. It simply returns a message saying the user is already connected, but it does not prevent the user from starting a new session. This allows users to bypass the concurrent session control and have multiple sessions at the same time. This can lead to issues such as session fixation attacks, where an attacker can hijack a user's session.

Steps

  • Implement a session management mechanism that tracks the number of active sessions per user.
  • When a user tries to authenticate, check if the maximum number of concurrent sessions for that user has been reached.
  • If the maximum number of sessions has been reached, deny the authentication request and display an appropriate error message.
  • If the maximum number of sessions has not been reached, allow the authentication request and create a new session for the user.
  • When a user tries to access a protected resource, check if the user has an active session.
  • If the user has an active session, allow access to the resource.
  • If the user does not have an active session, deny access to the resource and display an appropriate error message.

Compliant code

        import play.api.mvc._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

object Application extends Controller {
  private val MAX_SESSIONS = 1
  private var activeSessions = Map[String, Int]()

  def index = Action.async { request =>
    request.session.get("connected").map { user =>
      Future.successful(Ok("You are connected as " + user))
    }.getOrElse {
      Future.successful(Unauthorized("You are not connected"))
    }
  }

  def authenticate = Action.async { request =>
    request.session.get("connected").map { user =>
      if (activeSessions.getOrElse(user, 0) < MAX_SESSIONS) {
        activeSessions = activeSessions.updated(user, activeSessions.getOrElse(user, 0) + 1)
        Future.successful(Ok("You are now connected").withSession("connected" -> user))
      } else {
        Future.successful(BadRequest("Maximum number of concurrent sessions reached"))
      }
    }.getOrElse {
      Future.successful(Unauthorized("You are not connected"))
    }
  }
}
        
        

The updated code now includes a mechanism to track the number of active sessions per user. This is done using a mutable Map activeSessions that stores the username as the key and the number of active sessions as the value.

In the authenticate method, before creating a new session, the code checks if the user already has the maximum number of active sessions ( MAX_SESSIONS). If the user has reached the maximum, the authentication request is denied and an error message is returned. If the user has not reached the maximum, a new session is created and the number of active sessions for that user is incremented.

In the index method, which is used to access a protected resource, the code checks if the user has an active session. If the user does not have an active session, access to the resource is denied and an error message is returned. If the user has an active session, access to the resource is allowed.

This approach ensures that a user cannot bypass concurrent session control by going to any valid URL in the application when the error that there is already a session started appears.

References