Java-如何完全清除变量

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

我有一个Spring引导应用程序,它使用CredentialsService类将凭据存储为GuardString,并在其他类请求时返回它们

出现问题的地方在于,我们使用Checkmarx扫描代码并捕获潜在问题。如果用户名/密码的存储不再是问题,我仍然必须使用字符串变量来返回纯文本凭据。Checkmarx不喜欢这样,尤其是密码

这是CredentialsService的缩写视图:

@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是在运行时使用垃圾收集器运行的,所以您永远无法知道何时会从内存中删除值(除非您开始使用字符数组实现自己的内存管理)