Skip to content

CWE-470: Use of Externally-Controlled Input to Select Classes or Code ('Unsafe Reflection')

Overview

Unsafe reflection occurs when applications use untrusted input to select classes, methods, or code to instantiate/execute via reflection APIs (Class.forName, eval, import), enabling arbitrary code execution, object instantiation, and complete application compromise.

OWASP Classification

A05:2025 - Injection

Risk

Critical: Unsafe reflection enables remote code execution, instantiation of dangerous classes (Runtime, ProcessBuilder), method invocation on arbitrary objects, deserialization attacks, and complete server compromise through reflection-based exploits.

Relationship to Other CWEs

Remediation Steps

Core principle: Never allow untrusted input to select classes/types/methods for execution; map to an explicit allowlist or factory.

Locate the unsafe reflection vulnerability

  • Review the data_paths in security scan results to identify where untrusted input selects classes/code
  • Identify the source: where class name, method name, or code comes from (user input, HTTP parameters, external files)
  • Trace to the sink: Class.forName(), getMethod(), eval(), ScriptEngine.eval(), dynamic imports
  • Determine risk: what classes/methods could attacker instantiate or invoke

Use allowlist for class names (Primary Defense)

  • Define explicit allowlist of permitted classes: Map<String, Class<?>> ALLOWED_CLASSES = Map.of("user", UserHandler.class, "admin", AdminHandler.class)
  • Validate user input against allowlist: Class<?> clazz = ALLOWED_CLASSES.get(userInput); if (clazz == null) throw IllegalArgumentException
  • Never use user input directly: Don't pass user input to Class.forName(), getMethod(), eval() without validation
  • Use factory pattern instead: Map user input to predefined object instances, not reflection
  • Why this works: Attacker can only instantiate pre-approved, safe classes

Avoid reflection from user input entirely

  • Use factory pattern: Map<String, Supplier<Handler>> handlers = Map.of("type1", Type1Handler::new); Handler h = handlers.get(userInput).get()
  • Use strategy pattern: Define interface, map user input to implementation, no reflection needed
  • Use dependency injection: DI frameworks with predefined beans instead of dynamic class loading
  • Avoid eval() and script engines: Never evaluate user-supplied code with eval(), ScriptEngine

Validate fully qualified class names if reflection is unavoidable

  • Validate package name: if (!className.matches("^com\\.example\\.safe\\.[A-Za-z0-9]+$")) throw SecurityException
  • Verify inheritance: if (!SafeInterface.class.isAssignableFrom(clazz)) throw SecurityException
  • Check class annotations: Verify class has @Safe annotation or similar marker
  • Block dangerous classes: Reject Runtime, ProcessBuilder, FileWriter, URLClassLoader, ScriptEngine, Unsafe

Monitor and audit reflection usage

  • Log all reflection operations (class loading, method invocation, script evaluation)
  • Alert on reflection with untrusted input (attempts to load unauthorized classes)
  • Track allowlist effectiveness (requests blocked vs allowed)
  • Review code for new reflection usage in code reviews
  • Use static analysis to detect unsafe reflection patterns

Test the reflection fix thoroughly

  • Test with allowed class names (should work)
  • Test with dangerous classes: java.lang.ProcessBuilder, java.io.FileWriter, sun.misc.Unsafe (should be rejected)
  • Test with malformed class names: ../../../Evil, java.lang.Runtime (should be rejected)
  • Test package validation regex with bypass attempts
  • Verify factory pattern works for all legitimate use cases
  • Re-scan with security scanner to confirm the issue is resolved

Common Vulnerable Patterns

// Class name from user
String className = request.getParameter("handler");
Object handler = Class.forName(className).newInstance();

// Method invocation
String methodName = request.getParameter("action");
Method m = obj.getClass().getMethod(methodName);
m.invoke(obj);

// Script evaluation
String script = request.getParameter("code");
ScriptEngine engine = new ScriptEngineManager().getEngineByName("javascript");
engine.eval(script);  // RCE!

Attack Examples

// RCE via ProcessBuilder
?class=java.lang.ProcessBuilder

// File operations
?class=java.io.FileWriter

// Reflection attacks
?class=sun.misc.Unsafe

Additional Resources