从一次CPU打满到ReDos攻击和防范
作者:京东物流 刘海茂
近期碰到一起值班报警事件,web 应用服务器 CPU 消耗打到 99%,排查后发现是因为 ReDoS 导致了服务器发生了资源被耗尽、访问系统缓慢的问题,通过排查过程从而分享下 ReDos 攻击的原理、常见场景以及防范和解决方案,如果有错误欢迎指正。
背景
值班的时候突然报警,web 应用服务器 CPU 消耗打到 99%,同时现场反馈系统访问缓慢
登录泰山平台,查看 ump 监控发现系统消耗 CPU 消耗突然被打满
通过 java 自带的 dump 工具,下载 jstock 文件,发现有大量相同任务线程在运行,具体的堆栈信息如下
仔细查看这些线程的执行代码,发现都调用了 UrlUtil.extractDomain 这个方法
根据堆栈信息查看业务代码,发现是 joybuy 登录拦截器用正则表达式匹配访问 url 解析主域的方法出现了阻塞,至此,可以判断是因为 ReDoS 导致了服务器发生了资源被耗尽、访问系统缓慢的问题,那么,什么是 ReDoS 呢?
ReDos 简介
ReDoS 攻击(正则表达式拒绝服务攻击 (Regular Expression Denial of Service)),攻击者可构造特殊的字符串,导致正则表达式运行会消耗大量的内存和 cpu 导致服务器资源被耗尽。无法继续响应,那为何不确定的正则表达式会导致 redos 攻击呢?这得从正则表达式的实现原理说起
原理
目前实现正则表达式引擎的方式有两种
- DFA 自动机(Deterministic Finite Automaton,确定有限状态自动机)
- NFA 自动机(Nondeterministic Finite Automaton,非确定有限状态自动机)
- DFA 自动机的构造代价远大于 NFA 自动机,但 DFA 自动机的执行效率高于 NFA 自动机
- 假设一个字符串的长度为 n,如果采用 DFA 自动机作为正则表达式引擎,则匹配的时间复杂度为 O (n)
- 如果采用 NFA 自动机作为正则表达式引擎,NFA 自动机在匹配过程中存在大量的分支和回溯,假设 NFA 的状态数为 s,
- 则匹配的时间复杂度为 O(ns)
- NFA 自动机的优势是支持更多高级功能,但都是基于子表达式独立进行匹配
- 因此在编程语言里,使用的正则表达式库都是基于 NFA 自动机实现的
NFA 的特性:
一个有限的状态集合 S
一个输入符号集合 sigma,空字符 epsilon 不属于 Sigma
状态迁移函数 F,对于特定的输入字符和状态,输出对应的变更状态集合
4.s0 为初始状态
5.S 子集为结束状态集
说明
定义一个正则表达式 ^(a+)+$ 来对字符串 aaaaX 匹配。使用 NFA 的正则引擎,必须经历 2^4=16 次尝试失败后才能否定这个匹配。
同理字符串为 aaaaaaaaaaX 就要经历 2^10=1024 次尝试。如果我们继续增加 a 的个数为 20 个、30 个或者更多,那么这里的匹配会变成指数增长
常见 ReDoS 场景
以 java 为例,有以下几种常见的 ReDoS 场景:
1、使用 javax.validation.constraints.Pattern 验证入参是否合理的场景
/**
* 客户备注
* */
@ExcelProperty(index = 14)
@Length(min = 11 , max = 11, message = "VAT号必须为11位")
@Pattern(regexp = "^(GB)\d{9}", message = "VAT号必须以GB开头,9位数字结尾")
private String vatNumber;
2、使用 String.matches 进行业务数据验证的场景
//发票日期格式yyyy-MM-dd
String regExp = "^[1-9]\d{3}-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2][0-9]|3[0-1])$";
if (StringUtils.isNotBlank(outstockDto.getInvoiceDate()) && !outstockDto.getInvoiceDate().matches(regExp)){
totalMsg.add(new ErrorMsgDTO(ResultCodeEnum.OUTSTOCK_INVOICE_DATE_FORMAT_ERROR.getCode()));
}
3、使用 String.replaceAll 做参数替换的场景
private String getParamName(String str) {
if (PATTERN_START_END.matcher(str).matches()) {
String newStr = str.replaceAll("#\{", "").replaceAll("\}", "");
if (StringUtils.isEmpty(newStr)) {
return "";
} else if (newStr.contains(".")) {
return StringUtils.substringAfterLast(newStr, ".");
}
return newStr;
}
return null;
}
4、配置文件匹配参数的场景
# joybuy登录主域
joybuy.login.domain = .*fop.joybuy.com$
# 欧美B账号登录主域
pulsar.login.domain = .*ifop.jd.com$
ReDoS 检测
1、RegexStaticAnalysis 工具
测试方式如下:
使用 maven package 打包后执行本地运行,输入需要测试的正则表达式
2、在线测试地址:https://regex101.com/
测试方式:
直接在输入框输入正则表达式和需要测试的字符串,既可以看到对饮匹配的步数和结果
在 dubugger 模式下可以查看匹配的详细过程和步数
防范手段
防范手段只是为了降低风险而不能百分百消除 ReDoS 这种威胁。当然为了避免这种威胁的最好手段是尽量减少正则在业务中的使用场景或者多做测试,增加服务器的性能监控等
- 降低正则表达式的复杂度,尽量少用分组
- 严格限制用户输入的字符串长度
- 使用单元测试、fuzzing 测试保证安全
- 使用静态代码分析工具
- 增加性能监控,如 ump、pfinder 等
解决方法
了解了 ReDoS 的原理和防范,针对本次 CPU 的报警代码进行了优化,采用判断请求路径和分割字符串的方式获取访问的域,避免使用正则表达式导致的 ReDoS 问题
实际修复代码
public static String extractDomain(String url) {
if(StringUtils.isBlank(url)) {
return "";
}
int index = 0;
if(url.startsWith(HTTP)) {
index = HTTP.length();
} else if(url.startsWith(HTTPS)) {
index = HTTPS.length();
} else {
return "";
}
String safeUrl = url.substring(index);
index = safeUrl.indexOf('/');
if(index > 0) {
safeUrl = safeUrl.substring(0, index);
}
String[] array = safeUrl.split("\.");
if(array.length < 2) {
return "";
}
String part1 = array[array.length - 2];
String part2 = array[array.length - 1];
if(StringUtils.isNotBlank(part1) && StringUtils.isNotBlank(part2)) {
if(!isIn(part2, DOMAINS)) {
return "";
}
return part1 + '.' + part2;
}
return "";
}
从一次CPU打满到ReDos攻击和防范的更多相关文章
- JVM CPU占满问题定位
RASP加载后出现JVM CPU占满问题,jstack -F输出信息无法找到对应占用CPU的线程 perf定位到占用CPU的热代码位于Dependencies::find_finalizable_su ...
- Mybatis 并发执行导致cpu占满的问题
最近线上服务经常 出现cpu达到100%的问题,发现都是执行oracle操作的方法就没有返回.经过排查,最后定位到cpu消耗在以下方法 System.Collections.Generic.Dicti ...
- TODO:如何模拟cpu打满,磁盘打满,网卡打满
背景: 测试活动中,需要构造cpu打满.磁盘打满.网卡打满的场景 场景1:cpu打满 环境信息: 虚拟机,物理核数16个,每个物理核的虚拟核数1个,虚拟核数16个: [root@vm10-0-0-8 ...
- JVM CPU打满问题定位
1.线程不释放,导致Old区占满,系统不停的FullGC 发现应用并没有在进行FGC,而是进行频繁的YGC. YGC也存在异常,S1和S0区域都是从0直接跳到99% 观察堆大小装太发现Young区内存 ...
- 通过jstack与jmap分析一次cpu打满的线上故障
一.发现问题 下面是线上机器的cpu使用率,可以看到从4月8日开始,随着时间cpu使用率在逐步增高,最终使用率达到100%导致线上服务不可用,后面重启了机器后恢复. 二.排查思路 简单分析下可能出问题 ...
- mysql cpu 100% 满 优化方案
解决MySQL CPU占用100%的经验总结 - karl_han的专栏 - CSDN博客 https://blog.csdn.net/karl_han/article/details/5630782 ...
- mysql cpu 100% 满 优化方案 解决MySQL CPU占用100%的经验总结
下面是一些经验 供参考 解决MySQL CPU占用100%的经验总结 - karl_han的专栏 - CSDN博客 https://blog.csdn.net/karl_han/article/det ...
- 正则表达式回溯导致的CPU打满
参考: https://my.oschina.net/ttscjr/blog/2208526 https://mp.weixin.qq.com/s?__biz=MzA4MjIyNTY0MQ==& ...
- socket短连接太多,accept次数很多导致主线程CPU占满,工作线程CPU占用率低
1.使用epoll的ET模式: 2.开启reuseport方法: Linux 最新SO_REUSEPORT特性:http://www.mamicode.com/info-detail-2201958. ...
- 多事之秋-最近在阿里云上遇到的问题:负载均衡失灵、服务器 CPU 100%、被 DDoS 攻击
昨天 22:00~22:30 左右与 23:30~00:30 左右,有1台服役多年的阿里云负载均衡突然失灵,造成通过这台负载均衡访问博客站点的用户遭遇 502, 503, 504 ,由此给您带来麻烦, ...
随机推荐
- 快来,这里有23种设计模式的Go语言实现
摘要:设计模式(Design Pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结,使用设计模式是为了可重用代码.让代码更容易被他人理解并且保证代码可靠性. 本文分享自华 ...
- JVM内存模型,你看这一篇就够了
摘要:JVM是一种用于计算设备的规范,是一个虚构出来的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的. 本文分享自华为云社区<[云驻共创]JVM内存模型的探知之旅>,作者:多米 ...
- 云图说|图解DGC:基于华为智能数据湖解决方案的一体化数据治理平台
摘要:数据湖治理中心DGC,帮助企业快速构建从数据集成到数据服务的端到端智能数据系统,消除数据孤岛,统一数据标准,加快数据变现,实现数字化转型. 本文分享自华为云社区<[云图说]第232期 图解 ...
- CANN5.0黑科技解密 | 高并发图片视频处理,为出行保驾,为生活添彩!
摘要:华为推出昇腾AI基础软硬件平台(昇腾AI处理器+异构计算架构CANN),不仅能高效承接各类人工智能计算任务,还可两招解决以上图像处理面临的诸多问题. 四通八达的路网和车水马龙的盛景诠释着城市的繁 ...
- TS数据类型:从C++/Java/Python到TS看元组tuple—元组的来龙去脉
在C++有Java这种强类型语言中,常用的Array.List.Set ,在集合中只能是一种类型(可以复习下:<再谈Java数据结构-分析底层实现与应用注意事项). int data[] = n ...
- 差点错过!火山引擎VeDI帮这家企业成功挖掘200余条商机
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 与个体消费市场临时性需求大.决策参与人少等情况不同,企业消费市场往往因为长线需求复杂.商品/服务的价格高.参与决策 ...
- MySQL Select 语句执行顺序
一条 SQL 查询语句结构如下: SELECT DISTINCT <select_list> FROM <left_table> <join_type> JOIN ...
- 路由分发 路由别名的名称空间 虚拟环境 HTTPresponse JsonResponse request对象获取文件 CBV源码剖析 模板语法传值特性 模板语法过滤器
目录 路由分发 路由别名冲突 反向解析失败 方式一:名称空间 include 方式二:别名不冲突即可 虚拟环境 python -m venv venv_name 视图层之必会三板斧 HTTPrespo ...
- Make、Makefile、Cmake、QMake 的区别
本博文的简述or解决问题? make makefile cmake qmake都是什么,有什么区别? 查了一下好像是编译用的,既然是编译为什么我们不用g++. javac 来编译呢?我猜答案是方便一点 ...
- Educational Codeforces Round 96 (Rated for Div. 2) (A - C题个人题解)
因为火锅导致错过的上分机会,赛后发现人均AC5题 1430A. Number of Apartments 暴力搜索 #include<bits/stdc++.h> using namesp ...