携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情
accessToken的漏洞
Xxl-Job使用版本 2.2.0
关于accessToken 顾名思义是一种使用凭证,为了安全着想的.具体使用场景是在执行器与admin进行通信是使用:
- 执行器注册
- 执行器验证admin的请求是否安全 关于第二点是在admin向执行器发送请求在请求头中进行设置的.
public static final String XXL_JOB_ACCESS_TOKEN = "XXL-JOB-ACCESS-TOKEN";
而执行器收到请求后会对accessToken进行比对:
if (accessToken!=null
&& accessToken.trim().length()>0
&& !accessToken.equals(accessTokenReq)) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "The access token is wrong.");
}
accessToken漏洞分析
XXL-JOB为了灵活支持多语言以及脚本任务,提供了创新的 “GLUE模式”,该模式任务特点如下:
- 多语言支持:支持Java、Shell、Python、NodeJS、PHP、PowerShell……等类型。
- Web IDE:任务以源码方式维护在调度中心,支持通过Web IDE在线开发、维护。
- 动态生效:用户在线通过Web IDE开发的任务代码,远程推送至执行器,实时加载执行。
如上图所示,如果在 GLUE 模式任务代码中写入攻击代码,推送到执行器执行即可造成远程攻击。(问题点)
由于 XXL-JOB 官方版本原生自带鉴权组件,开启后可保障系统底层通讯安全。XXL-JOB 作者表示正常情况下调度中心与执行器底层通讯是安全的,不存在远程命令漏洞。但如果执行器未开启访问令牌,会导致无法识别并拦截非法的调度请求。恶意请求方可以借助 GLUE 模式,推送恶意攻击代码实现远程攻击。如下图所示:
解决方式: 开启 XXL-JOB 自带的鉴权组件 执行器.admin君配置 “xxl.job.accessToken”即可
XxlJob注解使用不当
当配置XxlJob注解对应的定时任务时,
/**
* 1、简单任务示例(Bean模式)
*/
@XxlJob("demoJobHandler")
public ReturnT<String> demoJobHandler() throws Exception {
XxlJobLogger.log("XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobLogger.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
return ReturnT.SUCCESS;
}
启动对应的执行器时发现应用直接被shutdown.
2022-08-10 14:05:23.712 ERROR pid:23584 [main] c.x.job.core.executor.XxlJobExecutor.stopEmbedServer[160] - null java.lang.NullPointerException: null at com.xxl.job.core.executor.XxlJobExecutor.stopEmbedServer(XxlJobExecutor.java:158) at com.xxl.job.core.executor.XxlJobExecutor.destroy(XxlJobExecutor.java:85) at com.xxl.job.core.executor.impl.XxlJobSpringExecutor.destroy(XxlJobSpringExecutor.java:55) at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:258) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:579) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:551) at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1056) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:512) at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1049) at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1057) at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1026) at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:975) at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:800) at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1204) at com.stnts.weplay.OrderStarter.main(OrderStarter.java:20) 2022-08-10 14:05:23.716 INFO pid:23584 [main] org.mongodb.driver.connection.info[71] - Closed connection [connectionId{localValue:4, serverValue:102228}] to 10.0.41.172:27018 because the pool has been closed. 2022-08-10 14:05:23.720 INFO pid:23584 [main] c.alibaba.druid.pool.DruidDataSource.close[2003] - {dataSource-1} closing ... 2022-08-10 14:05:23.723 INFO pid:23584 [main] c.alibaba.druid.pool.DruidDataSource.close[2075] - {dataSource-1} closed 2022-08-10 14:05:23.836 INFO pid:23584 [main] o.s.s.c.ThreadPoolTaskExecutor.shutdown[208] - Shutting down ExecutorService
探究执行器启动流程时发现: 启动入口: XxlJobSpringExecutor#afterSingletonsInstantiated
初始化XxlJob
注解的定时任务时: 若方法未定义参数,则抛出RuntimeException
异常导致应用停止
for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) {
Method method = methodXxlJobEntry.getKey();
XxlJob xxlJob = methodXxlJobEntry.getValue();
if (xxlJob == null) {
continue;
}
String name = xxlJob.value();
if (name.trim().length() == 0) {
throw new RuntimeException("xxl-job method-jobhandler name invalid, for[" + bean.getClass() + "#" + method.getName() + "] .");
}
if (loadJobHandler(name) != null) {
throw new RuntimeException("xxl-job jobhandler[" + name + "] naming conflicts.");
}
// execute method
if (!(method.getParameterTypes().length == 1 && method.getParameterTypes()[0].isAssignableFrom(String.class))) {
throw new RuntimeException("xxl-job method-jobhandler param-classtype invalid, for[" + bean.getClass() + "#" + method.getName() + "] , " +
"The correct method format like " public ReturnT<String> execute(String param) " .");
}
if (!method.getReturnType().isAssignableFrom(ReturnT.class)) {
throw new RuntimeException("xxl-job method-jobhandler return-classtype invalid, for[" + bean.getClass() + "#" + method.getName() + "] , " +
"The correct method format like " public ReturnT<String> execute(String param) " .");
}
method.setAccessible(true);
// init and destory
Method initMethod = null;
Method destroyMethod = null;
if (xxlJob.init().trim().length() > 0) {
try {
initMethod = bean.getClass().getDeclaredMethod(xxlJob.init());
initMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException("xxl-job method-jobhandler initMethod invalid, for[" + bean.getClass() + "#" + method.getName() + "] .");
}
}
if (xxlJob.destroy().trim().length() > 0) {
try {
destroyMethod = bean.getClass().getDeclaredMethod(xxlJob.destroy());
destroyMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException("xxl-job method-jobhandler destroyMethod invalid, for[" + bean.getClass() + "#" + method.getName() + "] .");
}
}
// registry jobhandler
registJobHandler(name, new MethodJobHandler(bean, method, initMethod, destroyMethod));
}
}
}
综上所述,任务对应方法配置需要满足以下几点:
- 方法形参只能有一个,且必须是String类型及其子类型
- 方法返回值必须是ReturnT及其子类型.
xxlJob
注解标注的init和destroy方法必须与该注解所对应方法在同一个类中,且必须无参.
定时任务过多导致线程数疯涨.
由于针对每个XxlJob
注解标注的定时任务都会对应一个单独的线程来执行该任务,那么一旦任务过多,且执行频繁,路由策略如果都选择再同一个节点和是哪个执行,那么势必导致该节点线程数过多,上下文切换频繁,导致cpu利用率不高,最终导致性能下降.