0

I recently stumbled over some problem of not being able to pass data the way I am used to.

Let's asume we create a provider class, that is instantiated and called by another consumer class which we can not modify. The provider provides the consumer with some instance of an object, that requires sensitive data. Clearly it is a bad practice to hard-code this data into our provider class. And in my case this sensitive data is generated at runtime and can not be hard-coded.

My idea was to give that provider class a private static field, a setter for that field and no getter. Now even if an instance of that provider class is created, we do not have access to that private static field, only the provider class itself can read it.


Let's provide some code. Asume we have the following Consumer class and Provider interface:

Consumer

public class Consumer {

    private final Class<? extends ProviderInterface> clazz;

    public Consumer(Class<? extends ProviderInterface> clazz) {
        this.clazz = clazz;
    }

    public void providerToSystemOut() throws IllegalAccessException, InstantiationException {
        System.out.println(clazz.newInstance().call().toString());
    }

}

ProviderInterface

public interface ProviderInterface {

    public Object call();

}

The Consumer only accepts the class of the consumer as parameter. So we do not can create that class ourselves to provide the sensitive data.

To pass our sensitive data we create the following provider and main:

Provider

public class Provider implements ProviderInterface {

    private static Object data = null;

    public static void setData(Object data) {
        Provider.data = data;
    }

    public Object call() {
        String str;
        if (data != null && (str = data.toString()).length() > 3) {
            Object object = "successfully received/processed sensitive data: "
                    + str.substring(0, 3)
                    + str.substring(3).replaceAll(".", "*");
            data = null;
            return object;
        } else {
            data = null;
            return "not enough data provided";
        }
    }
}

Main

public class Main {

    public static void main(String[] args) {

        Provider.setData("sensitive data");

        try {
            new Consumer(Provider.class).providerToSystemOut();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

My Question

This code works totally fine and seems to be secure. But I want to be sure if this is the best way to deal with this problem. Is the way I used a good practice? Is it secure? Is there some easy way for someone to get the sensitive data? If it may not be the best way, what would be better to do?

Some background

I faced this problem when dealing with selenide. Normally selenide creates its WebDriver instances itself. To provide it with a custom WebDriver, a Provider class is needed. Selenide than has a system property for the class name of that provider and uses this String to create an instance of the custom provider and to call the method which provides selenide with the WebDriver.

bchmnnjcb
  • 76
  • 4
  • What do you mean by "secure" in this context? One JVM or multiple ones? All values reside unencrypted in memory. – Michael Dec 04 '19 at 22:08
  • perhaps a partial contribution to the question [here](https://stackoverflow.com/questions/58515983/is-there-a-way-to-access-private-method-in-inner-class-with-or-without-reflectio/58516241#58516241) – Curiosa Globunznik Dec 04 '19 at 22:11
  • If the "sensitive data" is stored in code, then it can be extracted. You need to provide some kind of encoding to ensure that it is difficult to decode or reverse engineer – MadProgrammer Dec 04 '19 at 22:51

1 Answers1

0

Accessing data from static fields is actually easier to obtain via reflection, but in reality it does not matter whether it is static or private.

However you only really have to worry if the data you are storing is available to a client...

i.e) You are distributing your jar file to other people/clients for whatever reason.

If this is the case then there is no real way to prevent someone from accessing the data.

If you have access to the jar file, you can do something like below, to do whatever you want to your code.

public static void agentmain(String agentArgs, Instrumentation inst) {
    Class[] allYourClassesAtRuntime = inst.getAllLoadedClasses();
    for (Class yourClass : allYourClassesAtRuntime) {
        try {
            if (yourClass.getSimpleName().equals("Provider")) {
                Field yourClassField = yourClass.getField("data");
                yourClassField.setAccessible(true); //destroys private
                Object yourData = yourClassField.get(null);
                System.out.println("Got your data: " + yourData);
                //can do as you wish from here
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

The above method is invoked when an agent is attached to a JVM.

TL/DR: Your client sided data is never safe, so you should keep all things you wish to be secured on the server side and have something to negotiate between the two. If there is some scenario where you have to have some sensitive data on the client side, then don't keep any objects you don't want prying eyes to see outside of a constructor or method. Keep them local. Constructors are also slightly harder to decompile than a method.

Joe
  • 1,316
  • 9
  • 17
  • There are two ways to prevent reflective access to a private field: Build one’s program as a module in Java 9 or later, or install a [SecurityManager](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/System.html#setSecurityManager%28java.lang.SecurityManager%29). – VGR Dec 05 '19 at 01:49