NoSQL injection - Scala

NoSQL injection - Scala

Need

Prevention of NoSQL injection attacks

Context

  • Usage of Scala for building scalable and functional applications
  • Usage of play.api.libs.json for JSON parsing and manipulation in Play Framework
  • Usage of play.modules.reactivemongo.json.collection.JSONCollection for working with JSON collections in ReactiveMongo within Play Framework
  • Usage of play.modules.reactivemongo for integrating ReactiveMongo with Play Framework
  • Usage of reactivemongo.api.Cursor for querying and manipulating data in a reactive MongoDB database
  • Usage of BSONDocument from the reactivemongo.bson package for working with BSON documents in a reactive MongoDB application.

Description

Non compliant code

        import play.api.libs.json.Json
import play.modules.reactivemongo.json.collection.JSONCollection
import play.modules.reactivemongo.{MongoController, ReactiveMongoApi, ReactiveMongoComponents}
import reactivemongo.api.Cursor
import reactivemongo.bson.BSONDocument

def findUser(username: String) = Action.async {
  // Fetch the collection
  val futureCollection: Future[JSONCollection] = database.map(_.collection[JSONCollection]("users"))
  val query = Json.obj("username" -> username)
  
  // Find the user
  futureCollection.flatMap(_.find(query).cursor[JsObject]().collect[List]())
}
        
        

The above Scala code is a simple Play framework controller that interacts with a MongoDB NoSQL database using the ReactiveMongo library.

The findUser function is supposed to find a user in the "users" collection based on the provided username. However, it is vulnerable to NoSQL injection because it directly uses the string input username to construct a JSON query object without any sort of validation or sanitization.

An attacker could exploit this by providing a specially crafted string that modifies the query, potentially allowing them to fetch sensitive data or perform unauthorized actions. For example, they could provide a string like {$ne: null}, which would return all users because the query effectively becomes "find all users where username is not equal to null".

This is a serious vulnerability because it allows an attacker to manipulate the query and potentially access sensitive data. It's also a common mistake when developing applications that use NoSQL databases because developers often assume that they don't need to worry about SQL injection-like attacks. However, NoSQL databases have their own set of injection vulnerabilities that need to be addressed.

Steps

  • Validate and sanitize user inputs before using them in NoSQL queries.
  • Use parameterized queries or prepared statements to prevent NoSQL injection.
  • Implement proper access controls to limit the privileges of database operations.
  • Regularly update and patch the database and related libraries to fix any known vulnerabilities.
  • Implement logging and monitoring to detect and respond to any suspicious database activities.

Compliant code

        import play.api.libs.json.Json
import play.modules.reactivemongo.json.collection.JSONCollection
import play.modules.reactivemongo.{MongoController, ReactiveMongoApi, ReactiveMongoComponents}
import reactivemongo.api.Cursor
import reactivemongo.bson.BSONDocument
import org.apache.commons.lang3.StringUtils

def findUser(username: String) = Action.async {
  // Validate the input
  if (StringUtils.isBlank(username) || StringUtils.containsWhitespace(username)) {
    Future.failed(new IllegalArgumentException("Invalid username"))
  } else {
    // Fetch the collection
    val futureCollection: Future[JSONCollection] = database.map(_.collection[JSONCollection]("users"))
    val query = Json.obj("username" -> username)
  
    // Find the user
    futureCollection.flatMap(_.find(query).cursor[JsObject]().collect[List]())
  }
}
        
        

The updated code includes input validation using Apache Commons Lang, which is a library that provides helper utilities for the java.lang API, such as String manipulation methods.

The StringUtils.isBlank(username) checks if the username is either null, empty, or whitespace. The StringUtils.containsWhitespace(username) checks if the username contains any whitespace characters. If either of these conditions is true, the function will fail with an IllegalArgumentException.

This validation step helps to sanitize the user input and prevent NoSQL injection attacks by ensuring that the username does not contain any malicious code that could be executed in the NoSQL query.

In addition to this, it's recommended to implement proper access controls to limit the privileges of database operations, regularly update and patch the database and related libraries to fix any known vulnerabilities, and implement logging and monitoring to detect and respond to any suspicious database activities.

References