摘要:笔者在处理业务线问题时遇到接口返回的内容和实际内容不一致的现象。

本文分享自华为云社区《Java反射机制清空字符串导致业务异常分析》,作者:毕昇小助手。

编者按:笔者在处理业务线问题时遇到接口返回的内容和实际内容不一致的现象。根因是业务方通过Java反射机制将String类型敏感数据引用的value数组元素全部设置为’0’,从而实现清空用户敏感数据的功能。这种清空用户敏感数据的方法会将字符串常量池相应地址的内容修改,进而导致所有指向该地址的引用的内容和实际值不一致的现象。

背景知识

JVM为了提高性能和减少内存开销,在实例化字符串常量时进行了优化。JVM在Java堆上开辟了一个字符串常量池空间(StringTable),JVM通过ldc指令加载字符串常量时会调用 StringTable::intern 函数将字符串加入到字符串常量池中。

StringTable::intern函数代码

oop StringTable::intern(Handle string_or_null, jchar* name,
int len, TRAPS) {
unsigned int hashValue = hash_string(name, len);
int index = the_table()->hash_to_index(hashValue);
oop found_string = the_table()->lookup(index, name, len, hashValue);
// Found
if (found_string != NULL) {
ensure_string_alive(found_string);
return found_string;
}
debug_only(StableMemoryChecker smc(name, len * sizeof(name[0])));
assert(!Universe::heap()->is_in_reserved(name),
"proposed name of symbol must be stable");
Handle string;
// try to reuse the string if possible
if (!string_or_null.is_null()) {
string = string_or_null;
} else {
string = java_lang_String::create_from_unicode(name, len, CHECK_NULL);
}
#if INCLUDE_ALL_GCS
if (G1StringDedup::is_enabled()) {
// Deduplicate the string before it is interned. Note that we should never
// deduplicate a string after it has been interned. Doing so will counteract
// compiler optimizations done on e.g. interned string literals.
G1StringDedup::deduplicate(string());
}
#endif
// Grab the StringTable_lock before getting the_table() because it could
// change at safepoint.
oop added_or_found;
{
MutexLocker ml(StringTable_lock, THREAD);
// Otherwise, add to symbol to table
added_or_found = the_table()->basic_add(index, string, name, len,
hashValue, CHECK_NULL);
}
ensure_string_alive(added_or_found);
return added_or_found;
}

StringTable::intern 函数处理流程

字符串的创建方式

根据StringTable::intern函数处理流程,我们可以简单描绘如下6种常见的字符串的创建方式以及引用关系。

现象

某业务线使用fastjson实现Java对象序列化功能,低概率出现接口返回的JSON数据的某个属性值和实际值不一致的现象。正确的属性值应该为"null",实际属性值却为"0000"。

原因分析

为了排除fastjson自身的嫌疑,我们将其替换jackson后,依然会低概率出现同样的现象。由于两个不同三方件同时存在这个问题的可能性不大,为此我们暂时排除fastjson引入该问题的可能性。为了找到该问题的根因,我们在环境中开启远程调试功能。待问题复现,调试代码时我们发现只要是指向"null"的引用,显示的内容全部变成"0000",由此我们初步怀疑字符串常量池中的"null"被修改成"0000"。

一般导致常量池被修改有两种可能性:

  1. 第三方动态库引入的bug导致字符串常量池内容被修改;
  2. 在业务代码中通过Java反射机制主动修改字符串常量池内容;

业务方排查项目中使用到的第三方动态库,未发现可疑的动态库,排除第一种可能性。排查业务代码中使用到Java反射的功能,发现清空密码功能会使用到Java反射机制,并且将String类型密码的value数组元素全部设置为’0’。

业务出现的现象可以简单通过代码模拟:

  1. 在TestString对象类中定义一个nullStr属性,初始值为"null";
  2. 定义一个带有password属性的User类;
  3. 在main方法中创建一个密码为"null"的User对象,使用Java反射机制将密码字符串的所有字符全部修改为’0’,分别在密码修改前后打印TestString对象nullStr属性值;

复现代码

import java.lang.reflect.Field;
import java.util.Arrays;
public class TestString {
private String nullStr = "null";
public String getNullStr() {
return nullStr;
}
static class User {
private final String password;
User(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
}
private static void clearPassword(User user) throws Exception {
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] chars = (char[]) field.get(user.getPassword());
Arrays.fill(chars, '0');
}
public static void main(String[] args) throws Exception {
User user = new User("null");
TestString testString = new TestString();
System.out.println("before clear password >>>>");
System.out.println(" User.password:" + user.getPassword());
System.out.println("TestString.nullStr:" + testString.getNullStr());
System.out.println("--------------------------------");
clearPassword(user);
System.out.println("after clear password >>>>");
System.out.println(" User.password:" + user.getPassword());
System.out.println("TestString.nullStr:" + testString.getNullStr());
}
}

复现代码字符串引用关系如下图所示。

User对象的password属性和TestString的nullStr属性引用都同时指向常量池中的"null"字符串,"null"字符串的value指向 {‘n’,‘u’,‘l’,‘l’} char数组。使用Java反射机制将User对象的password属性引用的value数组全部设置为’0’,导致 TestString的nullStr属性值也变成了 “0000”。

输出结果如下:

  before clear password >>>>
User.password:null
TestString.nullStr:null
--------------------------------
after clear password >>>>
User.password:0000
TestString.nullStr:0000

通过输出结果我们可以发现在通过Java反射机制修改某一个字符串内容后,所有指向原字符串的引用的内容全部变成修改后的内容。

总结

在保存业务敏感数据时避免使用String类型保存,建议使用byte[]或char[]数组保存,然后通过Java反射机制清空敏感数据。

点击关注,第一时间了解华为云新鲜技术~

Java反射机制清空字符串导致业务异常分析的更多相关文章

  1. 详解Java反射机制

    反射是程序在运行状态下,动态的获取某个类的内部信息的一种操作.例如:类名,包名,所有属性的集合,所有方法的集合,构造方法的集合等.该操作发生在程序的运行时状态,所以编译器管不着有关反射的一些代码,通常 ...

  2. 一文带你了解Java反射机制

    想要获取更多文章可以访问我的博客 - 代码无止境. 上周上班的时候解决一个需求,需要将一批数据导出到Excel.本来公司的中间件组已经封装好了使用POI生成Excel的工具方法,但是无奈产品的需求里面 ...

  3. day11 Java反射机制

    java反射机制 反射是java中的动态机制,它允许我们在程序运行期间再确定类的实例化,方法的调用,属性的调用等,而不是传统意义上的在编码期间确定. 因此,反射可以大大的提高代码的灵活度,但是随之而来 ...

  4. 长篇图解java反射机制及其应用场景

    一.什么是java反射? 在java的面向对象编程过程中,通常我们需要先知道一个Class类,然后new 类名()方式来获取该类的对象.也就是说我们需要在写代码的时候(编译期或者编译期之前)就知道我们 ...

  5. JAVA反射机制—学习总结

    最近收到很多关于Java反射机制的问题留言,其实Java反射机制技术方面没有太多难点,或许是大家在学习过程中遗漏了细小知识点,导致一些问题无法彻底理解,现在我们简单的总结一下,加深印象.什么是反射机制 ...

  6. 转!!java反射机制

    Java 反射机制 基本概念 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法? 答案是肯定的. 这种动态获取类的信息以及动态调用对象 ...

  7. Java反射机制(创建Class对象的三种方式)

    1:SUN提供的反射机制的类: java.lang.Class<T> java.lang.reflect.Constructor<T> java.lang.reflect.Fi ...

  8. 【54】Java反射机制剖析

    java反射机制: 1.指的是可以于运行时加载,探知和使用编译期间完全未知的类. 2.程序在运行状态中, 可以动态加载一个只有名称的类, 对于任意一个已经加载的类,都能够知道这个类的所有属性和方法; ...

  9. 【java】-- java反射机制

    参考文章:https://blog.csdn.net/sinat_38259539/article/details/71799078    https://blog.csdn.net/wanderlu ...

  10. Java反射机制的使用(全)

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6566957.html  一:反射是什么 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有 ...

随机推荐

  1. 基于LangChain的LLM应用开发3——记忆

    此情可待成追忆,只是当时已惘然.我们人类会有很多或美好或痛苦的回忆,有的回忆会渐渐模糊,有的回忆午夜梦醒,会浮上心头. 然而现在的大语言模型都是没有记忆的,都是无状态的,大语言模型自身不会记住和你对话 ...

  2. LNOI 2023 游记

    Day -1 持续性的精神状态不太好,分明睡觉起床时间都没变,但白天就是非常非常困,为什么呢. 补不动任何题,脑子完全不转...... Day 0 13:30 才被家长叫醒,四点左右到了开发区还是好困 ...

  3. 聊聊RNN与Attention

    RNN系列: 聊聊RNN&LSTM 聊聊RNN与seq2seq attention mechanism,称为注意力机制.基于Attention机制,seq2seq可以像我们人类一样,将&quo ...

  4. Redis系列之常见数据类型应用场景

    目录 String 简单介绍 常见命令 应用场景 Hash 简单介绍 常见命令 应用场景 List 简单介绍 常见命令 应用场景 Set 简单介绍 常见命令 应用场景 Sorted Set(Zset) ...

  5. NLP技术如何为搜索引擎赋能

    在全球化时代,搜索引擎不仅需要为用户提供准确的信息,还需理解多种语言和方言.本文详细探讨了搜索引擎如何通过NLP技术处理多语言和方言,确保为不同地区和文化的用户提供高质量的搜索结果,同时提供了基于Py ...

  6. centos7安装glibc_2.28和gcc 8.2

    centos7默认的gcc版本是4.8.5,无法编译高版本的glibc 2.28,需要升级到gcc 8.2版本 注:gcc高版本和glibc 2.28不兼容 ## 查看自带默认的glibc strin ...

  7. go基础-泛型

    概述 在强类型变成语言中,类型是确定不可变,如函数入参是确定类型.链表元素是确定类型,这极大限制了函数功能.也有些解决方案,为每种类型都实现一版函数,会导致大量重复代码:使用类型转换,使用特殊形参(如 ...

  8. 提升效率,打通万里牛ERP与下游用友U8财务软件的无缝对接

    一.对接流程 1.1 销售/售后流程 在万里牛订单出库后,通过轻易云数据集成平台将数据推送至用友U8销售订单和销售出库单,这些单据可以进行关联操作. 当万里牛售后单完成退货入库后,通过数据集成平台将数 ...

  9. 旺店通·企业奇门与用友BIP旺店通销售出库单对接销售订单

    源系统平台:旺店通·企业奇门 源系统接口: 查询销售出库单wdt.stockout.order.query.trade 目标系统平台: 用友BIP目标系统接口: 销售订单单个保存/yonbip/sd/ ...

  10. 我的PyCharm为什么在linux下打不开?

    PyCharm打不开解决方案 本文基于Xrdp远程连接桌面环境,Unbutu Linux OS,解决办法仅供参考.应以实际情况为准. 问题产生的原因,Xrdp下GUI绘制依赖于Xrdp的渲染,当Xrd ...