0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Java反射机制清空字符串导致业务异常分析

openEuler 来源:openEuler 作者:openEuler 2022-06-22 11:17 次阅读

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

背景知识

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

  • StringTable::intern函数代码
    oopStringTable::intern(Handlestring_or_null,jchar*name,
    intlen,TRAPS){
    unsignedinthashValue=hash_string(name,len);
    intindex=the_table()->hash_to_index(hashValue);
    oopfound_string=the_table()->lookup(index,name,len,hashValue);
    
    //Found
    if(found_string!=NULL){
    ensure_string_alive(found_string);
    returnfound_string;
    }
    
    debug_only(StableMemoryCheckersmc(name,len*sizeof(name[0])));
    assert(!Universe::heap()->is_in_reserved(name),
    "proposednameofsymbolmustbestable");
    
    Handlestring;
    //trytoreusethestringifpossible
    if(!string_or_null.is_null()){
    string=string_or_null;
    }else{
    string=java_lang_String::create_from_unicode(name,len,CHECK_NULL);
    }
    
    #ifINCLUDE_ALL_GCS
    if(G1StringDedup::is_enabled()){
    //Deduplicatethestringbeforeitisinterned.Notethatweshouldnever
    //deduplicateastringafterithasbeeninterned.Doingsowillcounteract
    //compileroptimizationsdoneone.g.internedstringliterals.
    G1StringDedup::deduplicate(string());
    }
    #endif
    
    //GrabtheStringTable_lockbeforegettingthe_table()becauseitcould
    //changeatsafepoint.
    oopadded_or_found;
    {
    MutexLockerml(StringTable_lock,THREAD);
    //Otherwise,addtosymboltotable
    added_or_found=the_table()->basic_add(index,string,name,len,
    hashValue,CHECK_NULL);
    }
    
    ensure_string_alive(added_or_found);
    
    returnadded_or_found;
    }
    
    
  • StringTable::intern 函数处理流程

    8346ea3e-f150-11ec-ba43-dac502259ad0.png

  • 字符串的创建方式

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

835414ac-f150-11ec-ba43-dac502259ad0.png


		

现象

某业务线使用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属性值;

复现代码

importjava.lang.reflect.Field;
importjava.util.Arrays;

publicclassTestString{
privateStringnullStr="null";

publicStringgetNullStr(){
returnnullStr;
}

staticclassUser{
privatefinalStringpassword;

User(Stringpassword){
this.password=password;
}

publicStringgetPassword(){
returnpassword;
}
}

privatestaticvoidclearPassword(Useruser)throwsException{
Fieldfield=String.class.getDeclaredField("value");
field.setAccessible(true);
char[]chars=(char[])field.get(user.getPassword());
Arrays.fill(chars,'0');
}

publicstaticvoidmain(String[]args)throwsException{
Useruser=newUser("null");
TestStringtestString=newTestString();
System.out.println("beforeclearpassword>>>>");
System.out.println("User.password:"+user.getPassword());
System.out.println("TestString.nullStr:"+testString.getNullStr());
System.out.println("--------------------------------");
clearPassword(user);
System.out.println("afterclearpassword>>>>");
System.out.println("User.password:"+user.getPassword());
System.out.println("TestString.nullStr:"+testString.getNullStr());
}
}

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

83671084-f150-11ec-ba43-dac502259ad0.png

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

输出结果如下:

beforeclearpassword>>>>
User.password:null
TestString.nullStr:null
--------------------------------
afterclearpassword>>>>
User.password:0000
TestString.nullStr:0000

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

总结

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

后记

如果遇到相关技术问题(包括不限于毕昇 JDK),可以通过 Compiler SIG 求助。Compiler SIG 每双周周二举行技术例会,同时有一个技术交流群讨论 GCC、LLVM 和 JDK 等相关编译技术,感兴趣的同学可以添加如下微信小助手入群。

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    3859

    浏览量

    61296
  • JVM
    JVM
    +关注

    关注

    0

    文章

    152

    浏览量

    12126
  • 数组
    +关注

    关注

    1

    文章

    408

    浏览量

    25593

原文标题:Java反射机制清空字符串导致业务异常分析

文章出处:【微信号:openEulercommunity,微信公众号:openEuler】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    C语言字符串编译函数介绍

    在C语言中,字符串实际上是使用null字符O'终止的一维字符数组。因此,一个以null结尾的字符串,包含了组成字符串
    的头像 发表于 03-07 16:18 221次阅读
    C语言<b class='flag-5'>字符串</b>编译函数介绍

    USB字符串描述符里面的序列号字符串到底是什么东西?

    在设备描述符里面,有一个表示序列号字符串描述符的编号,请问这个序列号字符串的描述符是什么东西呢? 厂商字符串和设备字符串都能理解,而且在电脑的“设备与打印机”里面都找到了。但请问这个
    发表于 01-24 08:06

    labview扫描字符串怎么用

    介绍如何在 LabVIEW 中使用扫描字符串以及相关的技巧和注意事项。 字符串是 LabVIEW 中的一种基本数据类型,表示一系列字符的序列。扫描字符串是指从一个给定的
    的头像 发表于 12-29 10:12 556次阅读

    strlen和sizeof的异同

    的数量。注意事项:strlen需要在运行时遍历字符串,直到遇到空字符才停止计数。如果字符串没有以'\0'结尾,strlen可能导致未定义的行为。#include#i
    的头像 发表于 12-13 08:00 270次阅读
    strlen和sizeof的异同

    字符数组和字符串有没有区别?

    字符数组和字符串有没有区别?
    的头像 发表于 11-30 16:39 326次阅读

    java switch case 能不能用字符串

    Java的switch语句在处理字符串方面有一些限制。在Java 7之前,switch语句只能处理整数类型的表达式,如int或char。但从Java 7开始,
    的头像 发表于 11-30 14:46 571次阅读

    c语言字符串定义

    C语言是一种强大而广泛使用的编程语言,字符串是其中一个非常重要的概念。在C语言中,字符串是由一系列字符组成的数组,它可以表示文本、数字等各种类型的数据。在本文中,我们将详尽、详实、细致地介绍C语言
    的头像 发表于 11-24 10:02 829次阅读

    python输出固定长度的字符串

    中,可以使用字符串拼接来输出固定长度的字符串。首先,我们可以使用空字符串初始化一个变量,然后使用循环将制定长度的字符串拼接到该变量中,直到达到所需的长度。以下是一个示例代码: len
    的头像 发表于 11-22 10:41 1165次阅读

    java如何清空obj文件内容

    清空一个文件的内容可以通过以下步骤来完成。 首先,你需要指定要清空的文件的路径。你可以使用Java的File类来操作文件。以下是一个使用File类的例子: File file = new File
    的头像 发表于 11-21 10:29 211次阅读

    java字符串转化为日期格式

    Java中,字符串转化为日期格式是一个常见的需求。日期格式在处理时间相关的操作时非常重要,它可以用来表示一段时间的开始和结束,也可以用来计算时间差等。本文将详细介绍如何将一个字符串转化为日期格式
    的头像 发表于 11-17 16:38 705次阅读

    mysql字符串包含某个字符串

    MySQL是一种开源的关系型数据库管理系统,被广泛用于构建Web应用程序和其他大型数据驱动的应用。在进行MySQL数据库查询时,经常需要使用字符串包含操作,即判断一个字符串是否包含另一个字符串。本文
    的头像 发表于 11-16 14:52 992次阅读

    c语言如何对比字符串是否相同?

    c语言如何对比字符串是否相同把一个字符串变量直接赋值给另一个变量怎么实现
    发表于 11-02 07:45

    java字符分析

    上次聊到了 java字符流 Reader ,今天来看看它的好朋友 Writer 字符流。 Writer java 中的 IO 输出流不是只有 OutputStream 还有按
    的头像 发表于 10-10 15:38 344次阅读

    C++字符串string

    string是C++编程语言中的字符串。在C++中字符串处理可以使用c语言字符串形式char *,也可以使用string类格式。
    的头像 发表于 07-10 00:26 877次阅读
    C++<b class='flag-5'>字符串</b>string

    如何把字符串公式并得出结果?

    如图示一个字符串写的加减乘除运算公式,如何把字符串公式转换出来并得出结果?
    发表于 05-23 16:31