TL;DR
Attackers can use Java reflection to bypass access controls and call private methods or fields. This guide shows how attackers do it, and more importantly, how to protect your code.
Understanding the Attack
Java’s reflection API allows you to inspect and manipulate classes, methods, and fields at runtime. While powerful for legitimate uses (like frameworks), it can be abused to break encapsulation. Specifically, java.lang.reflect provides get* methods that allow access even when normal visibility rules would prevent it.
How Attackers Use Reflection
- Find the Target: The attacker identifies a class with sensitive data or functionality.
- Get the Method/Field: They use reflection to obtain references to private methods or fields within that class. For example:
Class> clazz = Class.forName("com.example.MyClass"); Method method = clazz.getDeclaredMethod("privateMethod"); - Bypass Access Checks: The attacker sets the accessibility of the method/field to
true:method.setAccessible(true); - Invoke/Modify: They then invoke the private method or modify the private field, bypassing normal security restrictions.
Object result = method.invoke(objectInstance, args);
Protecting Your Code
There’s no perfect solution, but these steps significantly increase the difficulty of a reflection attack.
1. Security Manager (Deprecated)
The SecurityManager used to be a primary defense. However, it’s deprecated in Java 9 and removed in later versions. It allowed you to define fine-grained access controls for reflection operations. If you’re using an older version of Java, consider if upgrading is possible before relying on this.
2. Module System (Java 9+)
The Java module system provides strong encapsulation. If your code is properly modularized, only explicitly exported packages are accessible to other modules. This limits the scope of reflection attacks.
- Export Only What’s Necessary: In your
module-info.javafile, export only the packages that need to be publicly accessible. - Use
requiresCarefully: Control which modules have access to your code.
3. Runtime Checks
Add checks within your methods to verify if the caller is authorized. This isn’t foolproof (reflection can still bypass it), but adds another layer of defense.
4. Code Obfuscation
Obfuscation makes it harder for attackers to understand and target specific methods or fields. Tools like ProGuard can rename classes, methods, and variables, making reflection-based attacks more difficult.
- Rename Identifiers: Change meaningful names to meaningless ones.
- String Encryption: Encrypt string literals that reveal sensitive information.
5. Limit Reflection Usage
Reduce the amount of reflection used in your own code. The less you rely on it, the smaller the attack surface.
6. Consider Using a Framework with Built-in Security
Some frameworks (like Spring) have built-in security features that can help mitigate reflection attacks.
Example: Runtime Check
public class MyClass {
private String secret = "sensitive data";
public void privateMethod() {
if (!isCallerAuthorized()) {
throw new SecurityException("Unauthorized access!");
}
// ... method logic ...
}
private boolean isCallerAuthorized() {
// Implement your authorization logic here.
return true; // Replace with actual check
}
}
Important Considerations
- Reflection is Difficult to Detect: It’s hard to reliably detect reflection attacks at runtime.
- Performance Impact: Runtime checks and obfuscation can impact performance.
- Defense in Depth: Use a combination of techniques for the best protection.

