Identify Code Injection Bugs in Your Code

First, you need to learn how to recognize a potential code injection vulnerability. In this section, we’ll discuss how injection vulnerabilities are introduced into code so that you’ll know what you shouldn’t do.

Code injections target applications where the functionality is created and interpreted during runtime based on user input. This makes finding possible attack points straightforward. In Node.js there are two interpreter functions to look out for: eval and Function. With these a developer can create a function out of string input and execute it at will.

The easiest way to avoid code injection attacks is to simply not create and evaluate code using user-submitted data. But using dynamically created code can be necessary in some cases and greatly simplify code in others. So instead of just saying “Never use user-submitted data in code construction,” let’s learn how to deal with it.

Maybe we’re writing a service where the user supplies a mathematical forumula and the app evaluates it. We could spend time creating a parser for the operands and operations and then interpret this intermediate form. Or we could simply expect a valid entry and evaluate it for JavaScript calculations:

 'use strict'​;
 
 var​ express = require(​'express'​);
 var​ bodyParser = require(​'body-parser'​);
 var​ app = express();
 
 app.get(​'/'​, ​function​(req, res){
 var​ form = ​''​ +
 '<form method="POST" action="/calc">'​ +
 '<input type="text" name="formula" placeholder="formula" />'​ +
 '<input type="submit" value="Calculate" />'​ +
 '</form>'​;
  res.send(form);
 });
 
 app.use(bodyParser.urlencoded({extended: ​false​}));
 
 app.post(​'/calc'​, ​function​ (req, res) {
 var​ result;
  eval(​'result = '​ + req.body.formula);
  res.send(​'The result is: '​ + result);
 });
 
 app.listen(3000);

This is a fairly simple example that works great, as long as the user submits valid calculations. A malicious individual could post something other than a mathematical calculation, however, such as 3; process.exit();, which would terminate the application. This would be a simple denial-of-service attack, which you’ll learn more about later, but it clearly demonstrates that we’ve lost control over our application and how much trust we’re putting in the user to submit valid data.

The first line of defense for injection attacks, and many other attack vectors, is to never trust user-submitted data. Any data coming from the user should be viewed as poisonous, and any data interacting with the input will also become poisonous. You need to worry both about data coming directly from user input and data pulled from the database, if it was originally submitted by the user.

The safest way is to check the input against a whitelist (not a blacklist) to verify that the data is in the expected format. It’s better to use whitelists than blacklists because it’s usually easier to identify all possible valid inputs than it is to come up with every single invalid one. Let’s extend the previous example to check the input to see if it contains anything other than numbers and operands. It’s a basic check and doesn’t eliminate all invalid inputs, but already the execution is much safer:

 app.post(​'/calc'​, ​function​ (req, res) {
 var​ formula = req.body.formula;
 // Check if there is anything else besides 0-9 - * + /
 if​(formula.match(​/​​[^​​0-9​​-/*+]​​/​)) {
  res.status(400).send(​'Invalid input'​);
 return​;
  }
 
 var​ result;
  eval(​'result = '​ + formula);
  res.send(​'The result is: '​ + result);
 });

While rejecting input that doesn’t conform to a required format is a valid solution, it isn’t the most convenient for the user who may want to insert spaces to make the text easier to read. The user probably won’t understand why the input was rejected. A more tolerant approach is to clean the input yourself, as shown in the example.

 app.post(​'/calc'​, ​function​ (req, res) {
 var​ formula = req.body.formula || ​''​;
 
 // Remove everything besides 0-9 - * + /
 var​ cleanFormula = formula.replace(​/​​[^​​0-9​​-/*+]​​/g​, ​''​);
 if​(cleanFormula.length < 1) {
  res.status(400).send(​'Invalid input'​);
 return​;
  }
 
 var​ result;
 // Surround with try-catch in case still invalid formula.
 try​ {
  eval(​'result = '​ + formula);
  } ​catch​(e) {
  res.status(400).send(​'Invalid input'​);
 return​;
  }
 // Say what we calculated
  res.send(​'The result of '​ + cleanFormula + ​' is: '​ + result);
 });

The code injection attack’s success factor depends on the system being targeted as well as the access rights assigned to the process. If the process has root privileges (which I’ve pointed out previously is a Very Bad Idea), the attacker would be able to take over the whole server and even use it to launch further attacks.

Another interesting method in the attacker’s arsenal is called server poisoning, where the attacker rewrites the server code as part of an injection attack to change the server’s behavior altogether.

Attackers can use server poisoning to steal information, such as modifying the application to send them an email containing the user’s password in plain text, or for monetary gains, such as changing how orders are calculated. You could lose money on every transaction and not know why.

Attackers can also rewrite server code in other interpreted languages such as PHP, but the approach is different to that with Node.js. In PHP, the attacker has to change the code file, so you can defend against server poisoning by making sure the code files on your server aren’t writable by the server process. Because Node.js treats functions as variables and runs in a single thread, the attacker doesn’t need to touch the file system to modify server behavior. This makes server poisoning attacks stealthier and harder to defend against in Node.js applications.

So far we’ve looked at how code injection attacks can be insiduous and stealthy and how to use input validation to protect the code. Next, we’ll look at another type of code injection attack that targets the server.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.54.108