Enforcement of concurrent session control
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.
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.