Unvalidated dynamic method call¶
ID: js/unvalidated-dynamic-method-call
Kind: path-problem
Security severity: 7.5
Severity: warning
Precision: high
Tags:
- security
- external/cwe/cwe-754
Query suites:
- javascript-code-scanning.qls
- javascript-security-extended.qls
- javascript-security-and-quality.qls
Click to see the query in the CodeQL repository
JavaScript makes it easy to look up object properties dynamically at runtime. In particular, methods can be looked up by name and then called. However, if the method name is user-controlled, an attacker could choose a name that makes the application invoke an unexpected method, which may cause a runtime exception. If this exception is not handled, it could be used to mount a denial-of-service attack.
For example, there might not be a method of the given name, or the result of the lookup might not be a function. In either case the method call will throw a TypeError
at runtime.
Another, more subtle example is where the result of the lookup is a standard library method from Object.prototype
, which most objects have on their prototype chain. Examples of such methods include valueOf
, hasOwnProperty
and __defineSetter__
. If the method call passes the wrong number or kind of arguments to these methods, they will throw an exception.
Recommendation¶
It is best to avoid dynamic method lookup involving user-controlled names altogether, for instance by using a Map
instead of a plain object.
If the dynamic method lookup cannot be avoided, consider whitelisting permitted method names. At the very least, check that the method is an own property and not inherited from the prototype object. If the object on which the method is looked up contains properties that are not methods, you should additionally check that the result of the lookup is a function. Even if the object only contains methods, it is still a good idea to perform this check in case other properties are added to the object later on.
Example¶
In the following example, an HTTP request parameter action
property is used to dynamically look up a function in the actions
map, which is then invoked with the payload
parameter as its argument.
var express = require('express');
var app = express();
var actions = {
play(data) {
// ...
},
pause(data) {
// ...
}
}
app.get('/perform/:action/:payload', function(req, res) {
let action = actions[req.params.action];
// BAD: `action` may not be a function
res.end(action(req.params.payload));
});
The intention is to allow clients to invoke the play
or pause
method, but there is no check that action
is actually the name of a method stored in actions
. If, for example, action
is rewind
, action
will be undefined
and the call will result in a runtime error.
The easiest way to prevent this is to turn actions
into a Map
and using Map.prototype.has
to check whether the method name is valid before looking it up.
var express = require('express');
var app = express();
var actions = new Map();
actions.set("play", function play(data) {
// ...
});
actions.set("pause", function pause(data) {
// ...
});
app.get('/perform/:action/:payload', function(req, res) {
if (actions.has(req.params.action)) {
if (typeof actions.get(req.params.action) === 'function'){
let action = actions.get(req.params.action);
}
// GOOD: `action` is either the `play` or the `pause` function from above
res.end(action(req.params.payload));
} else {
res.end("Unsupported action.");
}
});
If actions
cannot be turned into a Map
, a hasOwnProperty
check should be added to validate the method name:
var express = require('express');
var app = express();
var actions = {
play(data) {
// ...
},
pause(data) {
// ...
}
}
app.get('/perform/:action/:payload', function(req, res) {
if (actions.hasOwnProperty(req.params.action)) {
let action = actions[req.params.action];
if (typeof action === 'function') {
// GOOD: `action` is an own method of `actions`
res.end(action(req.params.payload));
return;
}
}
res.end("Unsupported action.");
});
References¶
OWASP: Denial of Service.
MDN: Map.
MDN: Object.prototype.
Common Weakness Enumeration: CWE-754.