>我有一个Spring Boot应用程序,它使用CredentialsService类将凭据存储为GuardedStrings,并在其他类请求时返回它们。
问题出现的地方在于我们使用Checkmarx来扫描我们的代码并捕获潜在问题。在用户名/密码的存储不再是问题的地方,我仍然必须使用 String 变量来返回纯文本凭据。Checkmarx不喜欢这样 - 特别是对于密码。
这是凭据服务的缩写视图:
@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());
然而,Checkmarx仍然不高兴。我使用相同的方法来存储 GuardedString 用户名和密码,并使用完全相同的方法来清除返回的字符串。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.
我已经尝试了各种各样的事情 - 一种在上次使用服务后销毁服务的最终方法,一种将所有变量显式设置为 null 并调用垃圾收集器的 disposeAll 方法。使用上面的代码,我在每个 get 方法中创建一个单独的线程,以便在将值返回给请求者时将"clear"变量设置为 null。虽然我可以确认这种最新方法确实为请求者提供了正确的值并将变量设置为 null,但似乎没有什么能让 Checkmarx 满意。
有人有什么想法吗?
提前谢谢。
D
一旦您将敏感数据放入像String
这样的不可变对象中,数据将在内存中保留很长时间。您可以释放变量,但即使没有物理引用,该值仍将位于内存中。您可以运行GC,它仍然存在。唯一有帮助的是使用相同的内存空间创建另一个变量并覆盖该值。
长话短说:只要您将密码放在String
,Checkmarx就会抱怨。
您可以做两件事:
- 您要么仅依赖
char[]
,要么在使用后清除数组, - 或者,如果您被迫并为您的案例请求特殊例外,请使用
String
值。
好吧,通过将密码作为常规String
返回/传输,您有点抛弃了将密码存储在GuardedString
中的所有价值。
对Checkmarx了解不多,但它只是一个代码扫描工具,所以很容易被愚弄。我建议实际解决问题,而不是试图将它们扫到地毯下。
- 请注意,构造函数
GuardedString
接受char[]
,而不是String
。这是第一个问题 - 您应该将密码从源头携带到这一点,作为char[]
- 更多关于它的信息 这里. - 不要将
String
退还给您的消费者 - 退回GuardedString
或至少一个char[]
。 - 取决于此类/库的目标消费者,但尝试为他们提供一种尽可能短的时间访问实际密码的方法,并在使用后重新学习
char[]
(以一种消费者不必自己这样做的方式,因为他可能会忘记(