我最近偶然发现了一些无法以我惯用的方式传递数据的问题。
假设我们创建了一个提供程序类,该提供程序类由另一个我们无法修改的使用者类实例化并调用。提供者为消费者提供了一些需要敏感数据的对象实例。显然,将这些数据硬编码到我们的提供程序类中是一种不好的做法。在我的情况下,此敏感数据是在运行时生成的,无法进行硬编码。
我的想法是为该提供程序类提供一个私有静态字段,该字段的设置器,而没有getter。现在,即使创建了该提供程序类的实例,我们也无法访问该私有静态字段,只有提供程序类本身才能读取它。
让我们提供一些代码。假设我们具有以下Consumer类和Provider接口:
消费者
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();
}
消费者仅接受消费者的类别作为参数。因此,我们不能自己创建该类来提供敏感数据。
为了传递敏感数据,我们创建了以下提供者和主要名称:
提供者
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();
}
}
}
我的问题
此代码可以正常工作,而且似乎很安全。但我想确定这是否是解决此问题的最佳方法。我采用的是一种好的做法吗?安全吗?有没有一种简单的方法可以使某人获取敏感数据?如果这不是最好的方法,那会更好吗?
某些背景
我在处理硒化物时遇到了这个问题。通常,selenide会自己创建其WebDriver实例。要为其提供自定义WebDriver,需要Provider类。然后Selenide具有该提供程序的类名的系统属性,并使用此String创建自定义提供程序的实例,并使用WebDriver调用向selenide提供的方法。
实际上,从静态字段访问数据更容易通过反射获得,但实际上,它是静态还是私有无关紧要。
但是您实际上只需要担心存储的数据是否可用于客户端...
即,您出于任何原因将jar文件分发给其他人/客户。
如果是这种情况,那么就没有真正的方法来阻止某人访问数据。
如果您有权访问jar文件,则可以执行以下类似操作,以对代码进行任何操作。
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();
}
}
}
当代理附加到JVM时,将调用上述方法。
TL / DR:客户端数据永远都不安全,因此您应将希望保护的所有内容保存在服务器端,并在两者之间进行协商。如果在某些情况下您必须在客户端上拥有一些敏感数据,那么不要将您不希望看到的任何对象保留在构造函数或方法之外。保持它们在本地。构造函数也比方法更难反编译。