Injections vulnerabilities are very common in web applications. This kind of vulnerability occurs when the application code accepts and processes untrusted user inputs as a part of a command query. Attackers exploit this vulnerability by injecting malicious code or accessing data without authorisation.

There are two main injection attacks: command injection and database injection. The former consists in injecting in the application malicious operating system commands that could allow the attacker reading or deleting restricted files or even install malware on the host computer.

Command Injection

The child_process Node.js module allows invoking programmatically underlying OS commands. The child_process.exec method is commonly used by Node.js developers to make system calls from their code. The exec method takes three arguments as shown in the code snippet below:

child_process.exec(command[, options][, callback])



The command argument is compulsory and has a string format, whereas the other two arguments are optional. options is an object and callback is a callback function that can be optionally passed to the exec method and that is called to carry out some tasks when exec terminates.

The possibility to pass a command as a string to the exec methods may rise security issues in the case a user input is used to construct the command.

For example, the code snippet below show using the child_process.exec method to list the content of a directory whose path is provided by a user in a URL.

child_process.exec (
  'ls' + req.body.dir_path, (error, data) => {
      if (error) {
        throw error
      }  
      console.log (data)  
  })



To exploit the injection vulnerability in the preceding code, an attacker can append to the dir_path input in the query string a malicious command such as & rm -rf /. This could delete all the files in the server after the ls command has been executed if the application is running with root privileges. This is possible for an attacker since, under the hood, the exec method spawns a new system shell and passes the command argument to it.

Preventing Command Injection

There are several ways to prevent command injection attacks in a Node.js application. When possible try to use spawn or execFile methods as described in the following code snippet:

// extract user input
let dir_path = req.body.dir_path

child_process.execFile (
  'ls',
  [dir_path],
  (error, data) => {
      if (error) {
        throw error
      }  
      console.log (data)  
  })

spawn and execFile forces to separate the arguments from the command. In addition, any malicious command chained to dir_path is collected into the execFile method second argument of which is of type array; this means that such commands are simply ignored or cause a syntax error.

Although execFile and spawn methods are safer alternatives to exec, they still may rise some security issue when such methods are used to invoke OS commands that allow passing options to enable file read or write (e.g. find, sed, etc.) or to execute custom scripts that process user inputs.

Injection attacks are basically possible due to insufficient or lacking input validation; thus, to protect against them it is a good practice to verify and validate user command arguments prior to executing them. When writing the validation logic, the best practice consist in following a whitelist approach. This means that the permitted options must be defined and all the user inputs that are not whitelisted must be rejected by the application.

Finally, the last practice to follow in order to mitigate injection attacks is to limit the application privileges. If an attacker succeed in injecting malicious command in your application, the injected commands are executed with the same OS-level privilege as the vulnerable application. A Node.js process should never run with root privileges but with a user that has access only to the required resources and no read or write access outside the application directory.

Database Injection

At Anywhere we use only NoSQL databases in our apps so I will restrict my discussion only to this type of databases putting a particular emphasis on MongoDB. To illustrate database injection attacks consider the find query illustrated in the following code snippet:

db.users.find ({
  username: uname,
  password: pwd
})

where uname and pwd are the user-supplied username and password. Now, suppose to inject in the find query the following object:

{
  username: 'admin',
  password: {$ne: ''}
}

This query will compare in the data base if an admin user exists with a password different from an empty string and will retrieve the admin user account data if that user exists. The same result can be achieved by using another comparison operator such as $gt in the query input. Attackers may also exploit the implicit vulnerability of the $where operator to perform database injection. Consider the following code snippet:

db.users.find ({
  active: true,
  $where: function () {
    return { this.age < user_input_age}
  }
})

In the previous example the user input user_input_age is used in an unsafe manner. If a user passes in the query the following valid JavaScript statements 0; while(1); the find query will run in an infinite loop and the database will be unresponsive.

Preventing NoSQL Database Injection

There are basically three ways to avoid NoSQL injection:

  1. Validating and escaping all the user-supplied inputs before using them in a query.
  2. Avoid using $where operator with JavaScriot functions that directly process user data.
  3. Using database accounts with the least privileges in order to mitigate potential damages in the case of successfull attacks.

Conclusions

It is possible to avoid or mitigate injection attacks using the safe coding practices described in this article. Regardless the injection type three steps must be followed:

  1. Avoid passing untrusted user data directly to a command interpreter.
  2. Always validate user inputs using the whitelist approach.
  3. Run your application or use database users with the least privileges.