Java-如何完全清除变量
我有一个Spring引导应用程序,它使用CredentialsService类将凭据存储为GuardString,并在其他类请求时返回它们 出现问题的地方在于,我们使用Checkmarx扫描代码并捕获潜在问题。如果用户名/密码的存储不再是问题,我仍然必须使用字符串变量来返回纯文本凭据。Checkmarx不喜欢这样,尤其是密码 这是CredentialsService的缩写视图:Java-如何完全清除变量,java,spring-boot,security,checkmarx,secure-coding,Java,Spring Boot,Security,Checkmarx,Secure Coding,我有一个Spring引导应用程序,它使用CredentialsService类将凭据存储为GuardString,并在其他类请求时返回它们 出现问题的地方在于,我们使用Checkmarx扫描代码并捕获潜在问题。如果用户名/密码的存储不再是问题,我仍然必须使用字符串变量来返回纯文本凭据。Checkmarx不喜欢这样,尤其是密码 这是CredentialsService的缩写视图: @Component public class CredentialsService { final
@Component
public class CredentialsService {
final ExecutorService executor = Executors.newSingleThreadExecutor();
private GuardedString customerApiPassword;
. . .
private StringBuilder clearCustomerApiPassword;
public CredentialsService( . . .
@Value("${customerapi.pwd}") String customerApiPassword,. . .) {
setCustomerApiPassword(customerApiPassword);
. . .
}
private void setCustomerApiPassword(String customerApiPasswordString) {
this.customerApiPassword = new GuardedString(customerApiPasswordString.toCharArray());
this.customerApiPassword.makeReadOnly();
}
public String getCustomerApiPasswordNo() {
clearCustomerApiPassword = new StringBuilder();
customerApiPassword.access(new GuardedString.Accessor() {
@Override
public void access(final char[] clearChars) {
clearCustomerApiPassword.append(clearChars);
}
});
customerApiPassword.dispose();
System.out.println("DGC: clearCustomerApiPassword is " + clearCustomerApiPassword);
Runnable clearFromMemory = () -> {
clearCustomerApiPassword = null;
System.out.println("DGC: clearCustomerApiPassword is " + clearCustomerApiPassword);
};
executor.execute(clearFromMemory);
return clearCustomerApiPassword.toString();
}
然后请求者通过以下方式访问所需的值:
IntegrationApiUtil.setBasicAuthKey(headers, credentialsService.getCustomerApiUsername(), credentialsService.getCustomerApiPassword());
然而,马克思仍然不快乐。我使用相同的方法存储GuardeString用户名和密码,并使用完全相同的方法清除返回的字符串。Checkmarx对用户名没有问题,但它仍然抱怨密码:
Method clearCustomerApiPassword; at line 24 of
src/main/java/com/.../service/CredentialsService.java
defines clearCustomerApiPassword, which is designated to contain user passwords. However, while plaintext
passwords are later assigned to clearCustomerApiPassword, this variable is never cleared from memory.
我尝试过各种方法——一个finalize方法在最后一次使用服务后销毁它,一个disposeAll方法显式地将所有变量设置为null并调用垃圾收集器。使用上面的代码,我在每个get方法中创建一个单独的线程,以便在我将值返回给请求者时将“clear”变量设置为null。虽然我可以确认,这种最新的方法确实为请求者提供了正确的值,并且还将变量设置为null,但似乎没有什么能满足Checkmarx的要求
有人有什么想法吗
提前谢谢
D一旦将敏感数据放入一个不可变对象(如
字符串
)中,数据将在内存中保留很长时间。您可以释放变量,但即使没有物理引用,该值仍将保留在内存中。你可以运行GC,它仍然会在那里。唯一有帮助的是使用相同的内存空间创建另一个变量并重写该值
长话短说:只要你把密码放在字符串中,Checkmarx就会抱怨
你可以做两件事:
- 您可以仅依赖于
char[]
并在使用后清除数组
- 或者,如果您被迫为您的案例请求特殊例外,请使用
字符串
值
好吧,通过将密码作为常规字符串
返回/传输,您有点放弃了将密码存储在GuardedString
中的所有价值
我对Checkmarx了解不多,但它只是一个代码扫描工具,所以很容易被愚弄。我建议实际解决问题,而不是试图掩盖问题
请注意,guaredString
构造函数接受char[]
,而不是字符串。这是第一个问题-您应该将密码作为char[]
-
不要将String
返回给您的消费者-返回guaredstring
或至少一个char[]
取决于此类/库的目标用户,但请尝试为他们提供一种方式,让他们在尽可能短的时间内访问实际密码,并在使用后清除char[]
(消费者不必自己这样做,因为他可能会忘记)
如果有答案,我真的很感兴趣。由于Java是在运行时使用垃圾收集器运行的,所以您永远无法知道何时会从内存中删除值(除非您开始使用字符数组实现自己的内存管理)