【问题标题】:Heap Inspection Security Vulnerability堆检查安全漏洞
【发布时间】:2015-08-01 04:59:36
【问题描述】:

我已经针对 checkmarx 工具运行我的 java 应用程序以发现安全漏洞,并且它不断地给出一个问题 - 堆检查,对于我使用字符数组的密码字段。除了指出密码字段的声明之外,它没有给出更多解释。

private char[] passwordLength;

谁能帮我解决这个问题,我还能寻找什么来解决这个问题?

【问题讨论】:

    标签: java security heap-memory static-code-analysis checkmarx


    【解决方案1】:

    在 JVM 内存中存储秘密信息的示例方法

    恕我直言,您应该使用SealedObject 在您的 JVM 内存中存储加密的凭证数据。

    您需要以下软件包:

    • java.security.SecureRandom
    • javax.crypto.Cipher
    • javax.crypto.KeyGenerator
    • javax.crypto.SealedObject
    • javax.crypto.SecretKey

    所以你创造

    • 创建密钥的初始化密钥生成器
    • 由密钥和安全随机初始化的密码
    • 然后您使用密码创建一个新的密封对象
    • 凭据的所有存储和(临时)加载都在替换您的 char 数组的密封对象中完成。

    可以在以下位置找到一个工作示例: https://github.com/Daimler/sechub/blob/3f176a8f4c00b7e8577c9e3bea847ecfc91974c3/sechub-commons-core/src/main/java/com/daimler/sechub/commons/core/security/CryptoAccess.java

    【讨论】:

      【解决方案2】:

      Checkmarx 堆检查安全漏洞 大家好,当我在 Spring 应用程序中使用 String 类型变量作为密码时,我遇到了这个问题。如下所示

        class User {
           private String username;
           private String password;
               //setter 
               //getter
           }
      

      然后为了解决这个问题,我做了以下步骤: 1. 创建如下 SecureString 类:

         import java.security.SecureRandom;
         import java.util.Arrays;
      
         /**
          * This is not a string but a CharSequence that can be cleared of its memory.
          * Important for handling passwords. Represents text that should be kept
         * confidential, such as by deleting it from computer memory when no longer
         * needed or garbage collected.
         */
      
        /**
        * Created by Devendra on 16/04/2020
        */
      
        public class SecureString implements CharSequence {
      
          private final int[] chars;
          private final int[] pad;
      
          public SecureString(final CharSequence original) {
              this(0, original.length(), original);
          }
      
          public SecureString(final int start, final int end, final CharSequence original) {
              final int length = end - start;
              pad = new int[length];
              chars = new int[length];
              scramble(start, length, original);
          }
      
          @Override
          public char charAt(final int i) {
              return (char) (pad[i] ^ chars[i]);
          }
      
          @Override
          public int length() {
              return chars.length;
          }
      
          @Override
          public CharSequence subSequence(final int start, final int end) {
              return new SecureString(start, end, this);
          }
      
          /**
           * Convert array back to String but not using toString(). See toString() docs
           * below.
           */
          public String asString() {
              final char[] value = new char[chars.length];
              for (int i = 0; i < value.length; i++) {
                  value[i] = charAt(i);
              }
              return new String(value);
          }
      
          /**
           * Manually clear the underlying array holding the characters
           */
          public void clear() {
              Arrays.fill(chars, '0');
              Arrays.fill(pad, 0);
          }
      
          /**
           * Protect against using this class in log statements.
           * <p>
           * {@inheritDoc}
           */
          @Override
          public String toString() {
              return "Secure:XXXXX";
          }
      
          /**
           * Called by garbage collector.
           * <p>
           * {@inheritDoc}
           */
          @Override
          public void finalize() throws Throwable {
              clear();
              super.finalize();
          }
      
          /**
           * Randomly pad the characters to not store the real character in memory.
           *
           * @param start start of the {@code CharSequence}
           * @param length length of the {@code CharSequence}
           * @param characters the {@code CharSequence} to scramble
           */
          private void scramble(final int start, final int length, final CharSequence 
          characters) {
              final SecureRandom random = new SecureRandom();
              for (int i = start; i < length; i++) {
                  final char charAt = characters.charAt(i);
                  pad[i] = random.nextInt();
                  chars[i] = pad[i] ^ charAt;
              }
          }
      
      }
      
      1. 创建自定义属性编辑器为:

        导入 java.beans.PropertyEditorSupport; 导入 org.springframework.util.StringUtils;

        public class SecureStringEditor extends PropertyEditorSupport {
        
        
           @Override
            public String getAsText() {
               SecureString  value =(SecureString) getValue();
               SecureString  secStr = new SecureString(value);
                return (value != null) ? secStr.asString() : "";
            }
        
            @Override
            public void setAsText(String text) throws java.lang.IllegalArgumentException {
                if (StringUtils.isEmpty(text)) {
                    setValue(null);
                } else {
                    setValue(new SecureString(text));
                }
            }
        }
        
      2. 将此自定义属性编辑器注册到 spring-bean.xml 文件为:

      【讨论】:

      • 第 3 步:
      • 之后使用 SecureString 类型而不是 String 作为密码。
      【解决方案3】:

      请参阅 security.stackexchange.com 上的this answer,了解“覆盖字符串中的值 char[] 是否更安全”。

      TLDR:你无能为力。

      PS:因为那是stackexchange的姊妹网站,所以我没有在这里复制答案(另外,它太长了)。如果版主不同意,请随意复制/粘贴。

      【讨论】:

        【解决方案4】:

        堆检查是关于存储在未加密的机器内存中的敏感信息,因此如果攻击者执行内存转储(例如 Heartbleed 错误),该信息就会受到损害。因此,仅仅持有该信息就会使其易受攻击。

        可以通过以安全方式存储此类敏感信息来缓解这种情况,例如使用 GuardedString 对象而不是字符串或字符数组,或者对其进行加密并在不久之后清除原始信息。

        有关详细信息,请参阅this CWE(描述 C/C++,但与 Java 具有相同的相关性)。

        【讨论】: