Is the problem of injection in javascript relevant?

Original author: Lukasz Makuch
  • Transfer
In the old days, when web development was based on the fact that server applications sent requests to relational databases and issued HTML on output, the following code was often encountered:

// ВНИМАНИЕ: Плохой пример!functionpopup(msg: string): string{
    return"<p class=\"popup\">" + msg + "</p>";
}

or this:

// ВНИМАНИЕ: Плохой пример!functiongetName(login: string): string{
    return"SELECT name FROM users WHERE login = \"" + login + "\"";
}

Since then, we have learned to use safer approaches.

Such tools as template engines and parameter binding are widely used. Today it is rare to find the dangerous string concatenation.

In this article I would like to share my thoughts on the attacks by introducing code. Apparently, they still pose a threat to javascript.



To understand why, we break one of the unsuccessful examples into simpler fragments. Like this:

functionf(userInput: A): A{
    const firstCommand: A = ...;
    const secondCommand: A = ...;
    return firstCommand.concat(userInput.concat(secondCommand));
}

Obviously, the root cause of attacks by injecting code is related to the fact that for a computer there is no difference between a command and user input! Therefore, an attacker can enter data that will later be processed as a code.

Naturally, as I said, there are well-known means of protection against such attacks. Instead of this:

"SELECT name FROM users WHERE login = \"" + login + "\""

better to write something like this:

query("SELECT name FROM users WHERE login = :login", {login})

Thus the team is SELECT name FROM users WHERE login =:loginclearly separated from the data {login}. At the same time, internal mechanisms ensure that the data will be prepared for use in the SQL query. Screen quotes and embed malicious code will not work.

However, web application development is rapidly evolving. Increasingly, it is not only this:

{
  paramA: "the value of the A parameter",
  paramB: "the value of the A parameter",
}

but also such:

{
  paramA: "the value of the A parameter",
  paramB: {$in: [
    "the value of the B parameter",
    "the value of the C parameter",
  ]},
}

The essential difference between the two options is that the parameter value is an object that includes the command!

Suppose the values ​​are read from userInput:

{
  paramA: userInput.paramA,
  paramB: {$in: [
    userInput.paramB[0],
    userInput.paramB[1],
  ]},
}

There is no need to worry that the user will provide a malicious string. Everything will be handled safely.

The problem is that the parameter values ​​can be not only simple values ​​like the value of the A parameter, but also commands, for example {$in: ["B", "C"]}. The user can send a request in various ways, after decrypting which an object is obtained (a form, JSON or XML), and therefore the code can be subjected to attacks by injection.

Assume userInput.paramAequal to {$empty: false}. Then the query looks like this:

{
  paramA: {$empty: false},
  paramB: {$in: [
    userInput.paramB[0],
    userInput.paramB[1],
  ]},
}

Again, it turns out that for a computer, reliable commands are indistinguishable from unreliable user input. Only now we are not talking about reliable and unreliable lines, but about reliable and unreliable objects.

To avoid this problem, you should always write commands so that they cannot be obtained from the user. This can be implemented including using the approach used in React and Snabbdom-Signature (this is a small library to protect against injections into the virtual DOM), namely, mark each command object Symbolso that it cannot be sent over the network.

I admit, and I have often thought that if there is no SQL database, or if I use a virtual DOM, attacks by introducing code do not threaten me. How wrong I was!


LOOKING.HOUSE - on the project collected more than 150 points looking glass in 40 countries. You can quickly execute the host, ping, traceroute, and mtr commands.



Also popular now: