Skip to main content

Data uniqueness not properly verified

Need

Ensuring proper validation of data uniqueness

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
  • Usage of play.api.libs.json for JSON parsing and manipulation in Play Framework
  • Usage of global execution context for Scala concurrent programming

Description

Non compliant code

import play.api.mvc._
import play.api.libs.json._
import scala.concurrent.ExecutionContext.Implicits.global

class UserController @Inject() (cc: ControllerComponents) extends AbstractController(cc) {

def createUser = Action.async(parse.json) { request =>
val userResult = request.body.validate[User]
userResult.fold(
errors => {
Future.successful(BadRequest(Json.obj("status" ->"KO", "message" -> JsError.toJson(errors))))
},
user => {
UserRepository.create(user).map { _ =>
Ok(Json.obj("status" ->"OK", "message" -> ("User '"+user.name+"' saved.") ))
}
}
)
}
}

The vulnerable part of the code is in the createUser method. When a POST request is made to this endpoint with a JSON body containing user information, the application attempts to parse the JSON into a User object and save it to the UserRepository.

However, there is no check to ensure that a user with the same information doesn't already exist in the repository. This could lead to duplicate user data in the repository, which is a violation of data uniqueness.

An attacker could take advantage of this vulnerability by repeatedly sending POST requests with the same user information, causing multiple identical users to be created in the UserRepository.

This vulnerability could be exploited to perform actions as the same user multiple times, or to cause confusion and errors in the application by creating multiple users with the same information.

Steps

  • Implement proper data uniqueness validation before creating a new user.
  • Check if the user already exists in the database before creating a new user.
  • Use a unique identifier, such as the user's email or username, to check for uniqueness.
  • If the user already exists, return an appropriate error response indicating that the user already exists.
  • Ensure that the uniqueness check is performed atomically to avoid race conditions.
  • Consider using database constraints or unique indexes to enforce data uniqueness at the database level.
  • Handle any exceptions or errors that may occur during the uniqueness validation process.

Compliant code

import play.api.mvc._
import play.api.libs.json._
import scala.concurrent.ExecutionContext.Implicits.global

class UserController @Inject() (cc: ControllerComponents) extends AbstractController(cc) {

def createUser = Action.async(parse.json) { request =>
val userResult = request.body.validate[User]
userResult.fold(
errors => {
Future.successful(BadRequest(Json.obj("status" ->"KO", "message" -> JsError.toJson(errors))))
},
user => {
UserRepository.findByUsername(user.username).flatMap {
case Some(_) => Future.successful(BadRequest(Json.obj("status" ->"KO", "message" -> "User already exists")))
case None => UserRepository.create(user).map { _ =>
Ok(Json.obj("status" ->"OK", "message" -> ("User '"+user.name+"' saved.") ))
}
}
}
)
}
}

The updated code now includes a check for user uniqueness before creating a new user. This is done by calling UserRepository.findByUsername(user.username). If a user with the same username already exists in the database, the application will return a BadRequest with a message indicating that the user already exists.

This check is performed atomically to avoid race conditions. If two requests to create a user with the same username are received at the same time, one of them will fail the uniqueness check and receive an error response.

This solution also handles any exceptions or errors that may occur during the uniqueness validation process. If an error occurs while checking for user uniqueness, the application will return a BadRequest with an appropriate error message.

In addition to this application-level check, consider using database constraints or unique indexes to enforce data uniqueness at the database level. This provides an additional layer of protection against data duplication.

References