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

    文章

    4424

    浏览量

    68156
  • JVM
    JVM
    +关注

    关注

    0

    文章

    161

    浏览量

    13112
  • 数组
    +关注

    关注

    1

    文章

    420

    浏览量

    27543

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

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

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    黑马-Java+AI新版V16零基础就业班百度云网盘下载+Java+AI全栈开发工程师

    默认结果,金融风控场景则更需要显式拒绝。此外,建立模型输出的运行时校验层,检测业务规则违例、特征合理性越界等情况,并可通过飞轮机制异常样本记录用于模型迭代。这些工程细节常被忽视,却直接决定了系统从
    发表于 05-01 11:29

    [完结15章]Java转 AI高薪领域必备-从0到1打通生产级AI Agent开发

    拒绝原地踏步:Java工程师AI转型的底层技术破局之道 在软件工程范式被大语言模型(LLM)彻底颠覆的今天,传统的“Java CRUD boy”正面临着前所未有的生存危机。当业务逻辑的生成可以被
    发表于 04-30 13:46

    飞凌嵌入式ElfBoard-环境变量之添加修改环境变量setenv

    ,不会出现由于字符串的生命周期导致环境变量失效的问题。1)头文件#include 2)函数原型int setenv(const char *name, const char *value, int
    发表于 03-26 09:24

    求助 LabVIEW 字符串比较

    请教大神 ,用一个字符串和多个字符串比较程序应该怎么做。 比如:A字符串和B字符串组(B组字符串个数不定可以是一个也可以是10个) 比较 A
    发表于 03-02 17:24

    打开工程后工程中的字体没有显示,如字符串,数字等控件不能预览显示字体?

    打开工程后工程中的字体没有显示,如字符串,数字等控件不能预览显示字体?
    发表于 02-25 17:39

    字符串控件与静态字符串控件中预览字符显示乱码,如何修改显示正常?

    字符串控件与静态字符串控件中预览字符显示乱码,如何修改显示正常?
    发表于 01-20 17:17

    字符串,数字控件如何控制背景颜色和前景字体颜色?

    字符串,数字控件如何控制背景颜色和前景字体颜色?
    发表于 01-20 15:12

    Linux下怎么让中文字符串按照拼音排序?

    求教 Linux 下怎么让中文字符串按照拼音排序?
    发表于 01-06 07:40

    RISC-V异常中断机制全解析

    RISC-V 作为开源、模块化的精简指令集架构,其异常中断机制是保障系统可靠运行、响应外部事件与处理内部错误的核心支撑。
    的头像 发表于 12-28 14:41 2223次阅读
    RISC-V<b class='flag-5'>异常</b>中断<b class='flag-5'>机制</b>全解析

    分析嵌入式软件代码的漏洞-代码注入

    字符串。在这种情况下,没有更多的参数,但机器在执行的时候并不了解这一点; 它所知道的全部是,函数的一些参数已经被推送到堆栈。 因为在C运行时没有机制可以告诉机器已经没有更多的参数了,所以printf将
    发表于 12-22 12:53

    字符串关联数字变量如何使用?我们的地址都是16位数据,可以使用16位数字变量显示字符串吗?

    字符串关联数字变量如何使用?我们的地址都是16位数据,可以使用16位数字变量显示字符串吗?
    发表于 12-15 08:24

    飞凌嵌入式ElfBoard-标准IO接口之格式化输出

    写入的字符数(不包括加在字符串末尾的结束空字符);如果出错,返回负值。5)示例:(将数据写入缓冲区,再写入标准输出)#include int main(){char buf[30];sprintf
    发表于 11-11 08:43

    labview如何生成一个带字符串返回的dll

    labview如何生成一个dll,如下图,要求一个输入,类型是字符串,返回类型也是字符串
    发表于 08-28 23:20

    在Python中字符串逆序有几种方式,代码是什么

    对于一个给定的字符串,逆序输出,这个任务对于python来说是一种很简单的操作,毕竟强大的列表和字符串处理的一些列函数足以应付这些问题 了,今天总结了一下python中对于字符串的逆序输出的几种常用
    的头像 发表于 08-28 14:44 1402次阅读

    harmony-utils之StrUtil,字符串工具类

    ,致力于助力开发者迅速构建鸿蒙应用。其封装的工具涵盖了APP、设备、屏幕、授权、通知、线程间通信、弹框、吐司、生物认证、用户首选项、拍照、相册、扫码、文件、日志、异常捕获、字符字符串、数字、集合
    的头像 发表于 07-03 11:32 927次阅读