Code Injection Using Eval Method¶
What does this mean ?¶
Code injection through eval occurs when untrusted data becomes executable source. The specific language and execution environment determine the available capabilities. A fixed expression that merely reads an existing variable is not automatically evidence that the variable's value has been compiled as code; review the actual source string passed to the evaluator.
What can happen ?¶
Evaluated source may read accessible data, alter program state or invoke operations available in that environment. A server-side evaluator runs with server process capabilities; browser evaluation runs within the page's browser context. Impact must be scoped accordingly rather than treating every example as a server compromise.
Recommendation¶
Remove dynamic source evaluation when the business task is data parsing or selection among known operations. Use JSON.parse/json.loads plus schema checks for JSON, not eval. For a limited operation selector, use a server-owned mapping or switch and validate the arguments.
Do not treat keyword filtering, escaping quotes or deleting a few built-ins as a general evaluator sandbox. Node's vm module explicitly is not a security mechanism for executing untrusted code. A product that intentionally executes customer code needs a separately designed isolation boundary and resource policy.
Sample Code¶
The example feature transforms a bounded piece of text using lower, upper or trim. The operation name is data and never becomes source. Case conversion follows each runtime's string rules; choose an explicit locale/Unicode policy if the application needs consistent cross-language behavior.
// Unsafe when the request controls source. Do not execute this fragment.
const result = eval(requestedSource);
function transformText(operation, value) {
if (typeof value !== 'string' || value.length > 4096) throw new Error('Invalid text');
switch (operation) {
case 'lower': return value.toLowerCase();
case 'upper': return value.toUpperCase();
case 'trim': return value.trim();
default: throw new Error('Unknown operation');
}
}
new Function(requestedSource) is also compilation of source, not a safe replacement.
# Unsafe for an untrusted expression. Do not execute this fragment.
result = eval(requested_source)
def transform_text(operation: str, value: str) -> str:
if not isinstance(value, str) or len(value) > 4096:
raise ValueError("Invalid text")
transforms = {"lower": str.lower, "upper": str.upper, "trim": str.strip}
if not isinstance(operation, str) or operation not in transforms:
raise ValueError("Unknown operation")
return transforms[operation](value)
Do not accept an arbitrary attribute or callable name through getattr as a substitute mapping.
// Unsafe: request-controlled PHP source is evaluated.
eval($requestedSource);
PHP 8+ fixed operation selection:
function transformText(string $operation, string $value): string {
if (strlen($value) > 4096) throw new InvalidArgumentException('Invalid text');
return match ($operation) {
'lower' => strtolower($value),
'upper' => strtoupper($value),
'trim' => trim($value),
default => throw new InvalidArgumentException('Unknown operation'),
};
}
Use the multibyte APIs if the product requires Unicode-aware case conversion. Calling a function whose name is taken directly from the request would reopen an execution-choice boundary.
# Unsafe for untrusted Ruby source. Do not execute this fragment.
result = eval(requested_source)
def transform_text(operation, value)
raise ArgumentError, 'Invalid text' unless value.is_a?(String) && value.length <= 4096
case operation
when 'lower' then value.downcase
when 'upper' then value.upcase
when 'trim' then value.strip
else raise ArgumentError, 'Unknown operation'
end
end
Unrestricted send/public_send based on client-selected method names is not the same as this fixed set of cases.
Regression checks¶
Confirm each allowed operation returns its intended result. Unknown operation names, object values and oversized strings must fail. Supply harmless source-looking text such as 2 + 3 as the value: it should remain data, not evaluate to 5. Verify no evaluator or dynamic callable lookup is reached. Test JSON parsing separately if that was the original use case.