Unsafe dynamic method access

Code injection through unsafe dynamic method access

Description

Using user input to select a function on window, globalThis, or global can invoke unintended functions. eval executes its string argument; Function creates a function that may be called later. If an attacker also controls the executed string, arbitrary code can run with the privileges of that environment.

Potential impact

  • Arbitrary JavaScript execution through code-executing functions
  • Exposure of accessible cookies, tokens, or environment variables
  • Changes to application state or misuse of privileged operations
  • Resource exhaustion through loops or expensive computations

Remediation

  • Do not let user input dynamically choose methods on global or function objects.
  • Register only allowed functions in a dedicated object or Map, and validate keys with checks such as Object.hasOwn before calling them.
  • If global access is necessary, use a narrow fixed allow-list such as encodeURIComponent and decodeURIComponent.
  • Reject names outside the allow-list and use a safe default behavior.
  • Exclude code-generating or string-executing functions such as eval, Function, setTimeout, setInterval, and execScript. Prefer JSON parsing, explicit branches, or a dedicated API map.
  • For additional protection, avoid 'unsafe-eval' in browser CSP. Where compatible, restrict string code generation in Node.js with --disallow-code-generation-from-strings and runtime or build policies.

Examples

Before

javascript
// Browser example
// Use the URL fragment as a global function name.
// https://site.example/#eval calls window["eval"]("alert('pwned')").
window.addEventListener('load', () => {
  const name = location.hash.slice(1); // User-controlled
  window[name]("alert('pwned')"); // Allows eval/Function calls.
});

// Node.js (Express) example
const express = require('express');
const app = express();
app.get('/do', (req, res) => {
  const action = req.query.action; // User-controlled
  // /do?action=eval executes the example string and changes global state.
  globalThis[action]("globalThis.exampleResult = 1"); // Vulnerable
  res.send('done');
});

After

javascript
// Restricted dispatch: dedicated API object, allowlist, and key checks.
// Object.create(null) removes inherited prototype members.
const API = Object.create(null);
API.ping = (d) => 'pong';
API.upper = (d) => String(d || '').toUpperCase();

function callApi(name, data){
  // Check own-key presence and function type.
  if (!Object.hasOwn(API, name) || typeof API[name] !== 'function') {
    throw new Error('invalid action');
  }
  return API[name](data);
}

// Browser example
window.addEventListener('load', () => {
  const name = new URL(location.href).searchParams.get('action');
  try {
    const result = callApi(name, 'hello');
    console.log(result);
  } catch (e) {
    console.warn('blocked');
  }
});

// Node.js (Express) example
const express = require('express');
const app = express();
app.get('/do', (req, res) => {
  try {
    const result = callApi(req.query.action, req.query.data);
    res.json({ ok: true, result });
  } catch {
    res.status(400).json({ ok: false, error: 'invalid action' });
  }
});

// Narrow global-function allowlist, excluding code execution.
const SAFE_GLOBALS = Object.freeze(["encodeURIComponent", "decodeURIComponent"]);

app.get('/encode', (req, res) => {
  const action = req.query.action;
  if (!SAFE_GLOBALS.includes(action)) {
    return res.status(400).send('invalid action');
  }
  res.send(globalThis[action](String(req.query.value || '')));
});

Explanation:

  • Before: User input selects a global function. Selecting eval executes the example's fixed string. Function only creates a function; its body runs when that function is called. Attacker control of the executed string could enable arbitrary code execution.
  • After: Register only allowed functions, validate own keys and function types, and reject unknown names. A null-prototype container avoids inherited members. Any necessary global-function allow-list must exclude code-generating functions.

References