开发规范—String类型保存敏感信息

问题简介

内存中的敏感信息使用结束后如果不及时清理,会存在敏感信息泄露的风险,应尽量减小敏感信息在内 存中的生命周期,使用结束后立即清0。Java中的 String 是不可变对象(创建后无法更改),使用 String 保存口令、秘钥等敏感信息时,这些敏感信息会一直在内存中直至被垃圾收集器回收(其生命 周期不可控),如果进程的内存被dump,会导致敏感信息泄露风险。

开发规范

内存中的敏感信息不能依赖垃圾回收机制的清理,而是在使用结束后主动将内存中的信息清0。为了方 便内存的清理,推荐优先使用 char[] / byte[] 存储敏感信息。对于必须使用String进行数据处理的场 景(如web系统获取请求数据、数据需要转为json字符串进行传递、接口中预定义使用String传递参数 等),不需要将String转为char[]这样的无效处理,但要对所有涉及敏感信息的String中的信息进行清 理,不要遗漏,例如将一个含敏感信息的对象转为json串,使用结束后要将对象中敏感信息及json串全 部清0。String的清理可以通过反射、调用JNI接口等方式实现

反例

void doSomething() {
String password = getPassword();
verifyPassword(password);
...
}
boolean verifyPassword(String pwd) {
...
}

正例(建议使用数组存储敏感信息,方便清理)

void doSomething() {
char[] password = getPassword();
verifyPassword(password);
// 清除password
Arrays.fill(password, (char) 0x00);
}
boolean verifyPassword(char[] pwd) {
...
}

正例(补救方案:三方件中的String清0)

void doSomething() {
...
String user = request.getParameter("username");
String password = request.getParameter("pwd");
verifyLoginInfo(user, password);
// 清除password
try {
Field valueFieldOfString = String.class.getDeclaredField("value");
valueFieldOfString.setAccessible(true);
char[] value = (char[]) valueFieldOfString.get(password);
Arrays.fill(value, (char) 0x00);
...
}
...
}

通用方法Demo

    private static void erasureString(String sensitiveString) {
        try {
            Field valueFieldOfString = String.class.getDeclaredField("value");
            valueFieldOfString.setAccessible(true);
            char[] value = (char[]) valueFieldOfString.get(sensitiveString);
            Arrays.fill(value, (char) 0x00);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private static  void erasureString(T model, Function getSensitiveString) {
        try {
            Field valueFieldOfString = String.class.getDeclaredField("value");
            valueFieldOfString.setAccessible(true);
            char[] value = (char[]) valueFieldOfString.get(getSensitiveString.apply(model));
            Arrays.fill(value, (char) 0x00);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private static  void erasureString(Collection list, Function getSensitiveString) {
        try {
            Field valueFieldOfString = String.class.getDeclaredField("value");
            valueFieldOfString.setAccessible(true);
            list.stream().forEach(model -> {
                char[] value = new char[0];
                try {
                    value = (char[]) valueFieldOfString.get(getSensitiveString.apply(model));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                Arrays.fill(value, (char) 0x00);
            });
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

实践结果

你可能感兴趣的:(开发规范—String类型保存敏感信息)