In this post I'll describe how OWASP Top 10 - A1 Injection applies to javascript based applications. Injection problems usually occur whenever unsanitized user data is concatenated with a static template to build a structure (typically a query of some kind).
This is the risk rating from OWASP:
Threat Agents | Attack Vectors | Security Weakness | Technical Impacts | Business Impacts | |
---|---|---|---|---|---|
______ | Exploitability EASY |
Prevalence COMMON |
Detectability AVERAGE |
Impact SEVERE |
______ |
Consider anyone who can send untrusted data to the system, including external users, internal users, and administrators. | Attacker sends simple text-based attacks that exploit the syntax of the targeted interpreter. Almost any source of data can be an injection vector, including internal sources | Injection flaws occur when an application sends untrusted data to an interpreter. Injection flaws are very prevalent, particularly in legacy code, often found in SQL queries, LDAP queries, XPath queries, OS commands, program arguments, etc. Injection flaws are easy to discover when examining code, but more difficult via testing. Scanners and fuzzers can help attackers find them. | Injection can result in data loss or corruption, lack of accountability, or denial of access. Injection can sometimes lead to complete host takeover. | Consider the business value of the affected data and the platform running the interpreter. All data could be stolen, modified, or deleted. Could your reputation be harmed? |
When you are accessing a database of some kind there is always the chance of SQL- or NoSQL-injection. To avoid
these problems, we should of course avoid building queries by string concatenation. Look for parameterized alternatives.
If you are using node.js with mysql, there you could use client.query() with parameters like:
client.query('SELECT id, user_name FROM user WHERE email=?', [email], ...)
In javascript based backends there is a far bigger problem than SQL. JSHint tells us that eval is evil
, and I tend to agree. Unsafe use of eval
- sticking unsanitized user data inside eval
statements - can lead to full compromize of the server.
Bryan Sullivan of Adobe did a talk at Blackhat USA 2011 called Attacking NoSQL and Node.js: Server-Side JavaScript Injection (SSJS), and this type of injection is also mentioned in Node.JS Security - the good, bad and ugly.
Consider a node.js application where the developer makes insecure use of eval to parse incoming JSON:
var http = require('http');
http.createServer(function (request, response) {
if (request.method === 'POST') {
var data = '';
request.addListener('data', function(chunk) { data += chunk; });
request.addListener('end', function() {
var stockQuery = eval("(" + data + ")");
var price = getStockPrice(stockQuery.symbol);
...
});
This leads to full remote code execution on the server. It's actually worse than SQL-injection. The attacker could send in code to read out local files:
var fs = require('fs');
return fs.readFileSync('/etc/passwd');
If the attacker is not able to directly read the response, we can also imagine types of blind eval injection. The attacker can send in while(1);
and easily overload the server's CPU. Alternatively the at could upload files and run them using child_process.spawn
, or send code that reads out the entire file system or the database (hello SQL-injection), and uploads it to a remote location.
To avoid this problem we should really just avoid sticking user data inside eval statements. At least void using string
concatenation. For JSON parsing we sould really stick to JSON.parse()
. If we really have to use eval for anything,
we could use a syntax like:
with({username: user.username, email: user.email}) {
result = eval("doSomething(username, email)");
}
Of course in the above example there is no reason to use eval, but you get the point. Access variables instead of using string concatenation.
Typical use ofeval
involves trying to set dynamic properties or invoke dynamic methods on objects. A typical eval statement thus looks something like:
eval("user." + propertyName + " = '" + propertyValue +"'"); //Set property with dynamic name
eval("user." + methodName + "()"); // Invoke method dynamically
It is really easy to avoid using eval
in this example. We can just use array lookups instead.
user[propertyName] = propertyValue; //Set property with dynamic name
user[methodName](); // Invoke method dynamically
On the client side, unsafe use of use input can also result in injection issues. We'll leave out HTML injection and XSS for now, and cover that in the next post.
JSON injection can happen if JSON is built by string concatenation before being sent to the server:
'{value1:"' + value + '"}'
In this case value
could escape the quotes and inject new key value pairs:
'{value1:"hello",value2:"world"}'
Similarly this can happen when building URLs in an unsafe manner:
url: "http://example.com/search/" + searchTerm
This could lead to URLs like:
url: "http://example.com/search/../something/evil"
Similarily we can have the same problem for URL parameters:
url: "http://example.com/search?q=" + searchTerm
which could lead to:
url: "http://example.com/search?q=notmuch&someother=parameter"
An example of a bug like this on AOL can be seen in the end of the DOMinator tutorial video from
Minded Security.
Contact me on twitter and I'll be happy to add (with attribution).