Skip to main content

NoSQL injection

Need

Prevention of NoSQL injection attacks

Context

  • Usage of PHP 7.0 for server-side scripting and web development
  • Usage of the App\User module for user-related functionality
  • Usage of Illuminate\Http\Request for handling HTTP requests in Laravel

Description

Non compliant code

<?php

use App\\User;
use Illuminate\\Http\\Request;

public function getUser(Request $request)
{
$user = $request->input('user');
$password = $request->input('password');

$user = User::where('user', $user)->where('password', $password)->first();

return response()->json($user);
}

?>

The above PHP code is a basic example of a NoSQL injection vulnerability. This is a method in a Laravel controller that handles a user login request. It takes a username and password from the request input without any validation or sanitization. It then uses these values directly in a NoSQL query to find a matching user in the database.

Here's how the code works:

  1. It uses the Request object's input method to get the user-provided username and password.
  2. It then constructs a NoSQL query using the where method, which creates a condition in the NoSQL query based on the user-provided username and password.
  3. The first method executes the query and returns the first result.

The vulnerability here is that a malicious user could potentially manipulate the user and password values in such a way that it modifies the query itself. This could allow them to bypass the login mechanism or reveal information they should not have access to. This is known as NoSQL injection.

For example, a malicious user could send a request with a user value of { "$ne": null } and a password value of { "$ne": null }. This would result in a query that returns the first user where the username and password are not null, effectively bypassing the login mechanism.

This vulnerability could be fixed by validating and sanitizing the user input before using it in the NoSQL query.

Steps

  • Use prepared statements with parameterized queries. This will ensure that the parameters (inputs) are sent separately from the query and are not directly included within the query.
  • Validate, sanitize, and escape user inputs. This will help to prevent malicious data from being inserted into the database.
  • Implement a strong password hashing mechanism. Storing passwords in plain text is a security risk. Use PHP's built-in functions like password_hash() and password_verify() to handle password security.
  • Limit the privileges of the database account used by the application. The account should only have the minimum permissions necessary to perform its duties.
  • Implement proper error handling that does not reveal details about the database or application structure.

Compliant code

<?php

use App\\User;
use Illuminate\\Http\\Request;
use Illuminate\\Support\\Facades\\Hash;

public function getUser(Request $request)
{
$request->validate([
'user' => 'required',
'password' => 'required',
]);

$user = $request->input('user');
$password = $request->input('password');

$user = User::where('user', $user)->first();

if (!$user || !Hash::check($password, $user->password)) {
return response()->json(['error' => 'The provided credentials are incorrect.'], 403);
}

return response()->json($user);
}

?>

The updated code includes several security improvements to prevent NoSQL injection attacks:

  1. Input Validation: The $request->validate() function is used to ensure that the 'user' and 'password' fields are present in the request. If not, a validation exception will be thrown, and a proper error response will be returned.

  2. Parameterized Queries: The where method in Laravel's query builder uses parameter binding under the hood, which protects against NoSQL injection. The user input is not directly included in the query string, but is sent separately, preventing attackers from manipulating the query.

  3. Password Hashing: Instead of directly comparing the provided password with the one stored in the database, the Hash::check function is used. This function will hash the provided password in the same way as the stored password hash was created, and then compare the two hashes. This way, the actual password is never directly used in the query, and passwords are not stored in plain text in the database, which is a significant security improvement.

  4. Error Handling: If the user is not found, or if the password does not match, a JSON response with an appropriate error message is returned. This does not reveal any details about the database or application structure.

Remember to always use a limited privilege database account for your application, which only has the necessary permissions to perform its duties. This is a good security practice to limit potential damage in case of a security breach.

References