Java-如何完全清除变量

时间:2020-06-29 20:24:52

标签: java spring-boot security checkmarx secure-coding

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

出现问题的地方是我们使用Checkmarx扫描代码并发现潜在问题。在用户名/密码存储不再是问题的地方,我仍然必须使用String变量来返回纯文本凭据。 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());

但是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.

我已经尝试了各种各样的方法-一种最后一次使用服务后销毁服务的finalize方法,将所有变量显式设置为null并调用垃圾收集器的disposeAll方法。使用上面的代码,我在每个get方法中创建一个单独的线程,以在将值返回给请求者时将“ clear”变量设置为null。虽然我可以确认这种最新方法确实为请求者提供了正确的值,并且也将变量设置为null,但似乎没有什么能让Checkmarx满意。

有人有什么想法吗?

谢谢。

D

2 个答案:

答案 0 :(得分:3)

好吧,您通过将密码作为常规GuardedString进行返回/传输,已经抛弃了在String中存储密码的所有价值。

对Checkmarx不太了解,但是它只是一个代码扫描工具,因此很容易上当。我建议实际解决这些问题,而不是试图将它们扫在底下。

  1. 请注意,GuardedString构造函数接受char[],而不是String。这是第一个问题-您应该将密码从源头一直保存为char[]-more about it here.
  2. 不要将String退还给您的消费者-退回GuardedString或至少退回char[]
  3. 取决于您要使用的此类/库的消费者,但是请尝试提供一种方式,让他们在尽可能短的时间内访问实际密码,并在使用后清除char[](以一种方式该消费者将不必自己做,因为他可以忘记)

答案 1 :(得分:1)

一旦您将敏感数据放入String之类的不可变对象中,该数据将在内存中保留很长时间。您可以释放该变量,但是即使没有物理引用,该值仍将位于内存中。您可以运行GC,它将仍然存在。唯一有帮助的是使用相同的内存空间创建另一个变量并覆盖该值。

长话短说:只要将密码放在String中,Checkmarx就会抱怨。

您可以做两件事:

  • 您要么仅依靠char[]并清除一次使用的数组,
  • 或如果您被迫为案件要求特殊例外,则使用String值。
相关问题