微信公众号:PersistentCoder关注可了解更多的教程。问题或建议,请公众号留言。
xxl-job作为一款在国内比较流行并且广泛使用的开源分布式任务调度框架,如果是部署在内网环境,没太大安全问题,但是我相信有相当一部分企业和团队以及个人项目,是将其部署在公网环境使用的,那么问题就来了,xxl-job-admin和xxl-job-executor部署在公网环境都存在比较多的安全问题,比如弱口令问题、控制台爆破、token泄露等。
本篇文章我们重点介绍一下xxl-job-admin的安全改造思路和实现方式。主要涉及到的改造点和升级内容如下:
使用在线工具生成比较复杂的accessToken:
或者使用java代码生成不规律的相对复杂的内容即可:
System.out.println(UUID.randomUUID().toString().replaceAll("-","").toUpperCase());
171D24AC040E4D4FA3AB04FB5B950C6E
然后在xxl-job-admin的配置文件中替换掉默认accessToken:
### xxl-job, access token
xxl.job.accessToken=uqCkJP#xB4mmiPZ3WIVvO9sQP78aXd!Y
需要注意的是,需要把accessToken同步给使用调度中心的业务服务相关参与方,不然xxl-job-admin与xxl-job-executor持有不同的密钥,双方无法通信。
这一步的改造是避免有心者使用默认accessToken结合网络空间搜索引擎扫出来公网开放出来9999默认端口盲扫攻击xxl-job-executor。
添加谷歌authenticator otp验证主要用于防护控制台弱口令问题、密码泄露问题、以及burpsuite爆破等。
拉下来项目代码后,在xxl-job-admin的pom文件中添加otp相关依赖:
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>java-otp</artifactId>
<version>0.4.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.16.0</version>
</dependency>
编写工具类用于生成谷歌验证码和校验谷歌验证码:
public static String generatorSecretKey(){
try {
KeyGenerator generator = KeyGenerator.getInstance("HmacSHA1");
int macLengthInBytes = Mac.getInstance("HmacSHA1").getMacLength();
generator.init(macLengthInBytes * 8);
SecretKey secretKey = generator.generateKey();
return new Base32().encodeAsString(secretKey.getEncoded());
}catch (Exception e){
logger.error("生成谷歌验证码失败", e);
return null;
}
}
public static boolean validateGoogleCodeLogin(String googleCode,String secretKey){
String currentCode = null;
try {
TimeBasedOneTimePasswordGenerator generator = new TimeBasedOneTimePasswordGenerator();
byte[] decodedKey = new Base32().decode(secretKey);
currentCode = generator.generateOneTimePasswordString(
new SecretKeySpec(decodedKey, 0, decodedKey.length, "HmacSHA1"),
Instant.now()
);
return Objects.equals(googleCode,currentCode);
}catch (Exception e){
logger.error("validateGoogleCodeLogin occur error;googleCode={},currentCode={},secretKey={}",googleCode,currentCode,secretKey,e);
return false;
}
}
登录页面添加谷歌验证码输入框,修改login.ftl模板:
改造初始化脚本,或者已经初始化之后在user表添加secrety_key字段,用于存储用户维度的google authenticator秘钥:
CREATE TABLE `xxl_job_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '账号',
`password` varchar(50) NOT NULL COMMENT '密码',
`role` tinyint(4) NOT NULL COMMENT '角色:0-普通用户、1-管理员',
`permission` varchar(255) DEFAULT NULL COMMENT '权限:执行器ID列表,多个逗号分割',
`secret_key` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '谷歌验证器key',
PRIMARY KEY (`id`),
UNIQUE KEY `i_username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
修改登录接口,添加谷歌验证码入参:
@RequestMapping(value="login", method=RequestMethod.POST)
@ResponseBody
@PermissionLimit(limit=false)
public ReturnT<String> loginDo(HttpServletRequest request
, HttpServletResponse response
, String userName
, String password
,String googleCode
, String ifRemember){
boolean ifRem = (ifRemember!=null && ifRemember.trim().length()>0 && "on".equals(ifRemember))?true:false;
return loginService.login(request, response, userName, password,googleCode, ifRem);
}
添加验证码校验:
这样在登录的时候就实现了谷歌验证码otp校验,能解决弱口令和burpsuite类似工具的爆破问题。
改造完的登录页面如下:
除了改造登录接口和页面,还要改造下添加用户的接口,管理员添加用户的时候自动生成google authenticator密钥然后保存,这样非admin用户登录的时候才具备google authenticator验证的能力。
另外插一嘴题外话,xxl-job的源码写的很那个~,并且不规范,一会儿api->service->dao,一会儿api直接调dao操作数据,长时间从事后端研发的大佬应该明白我表述的事情,所以有漏洞正常。
还是那句话,任务触发的过程叫调度任务,一定是调度中心触发调度,任务有具体实现者执行,也就是任务就是任务,一定是由业务属性和含义的,是不是交给业务服务来实现更合理,通过脚本托管到调度中心不怎么合适,带来了巨大的安全隐患,有人可能会说可能是为了支持多语言,我看未必,使用xxl-job-admin作为调度中心的绝大多数技术栈还都是java。
当然不管是禁用还是控制,都涉及到xxl-job-admin和xxl-job-executor,本篇主要介绍控制台安全升级,主要介绍下禁用和控制脚本任务的创建。
在xxl-job-admin的配置文件中添加控制是否开启脚本任务的属性:
### custom properties
job.allow.script=false
页面上任务列表和新增任务的运行模式的显示控制,由/jobinfo接口实现:
我们把控制是否开启脚本任务的属性注入,然后根据属性控制运行模式的展示:
默认是关闭状态,如果不开启则只展示BEAN类型的任务,如果开启则按照原来的方式展示所有任务类型。
多说一嘴,这里不能根据任务类型枚举中的isScript过滤,GLUE(Java)虽然定义成非脚本任务,但是也可以在控制台编辑,不需要在xxl-job-executor实现:
这里只是控制了前端创建任务入口,还需要在后端创建任务接口改造限制任务类型。
在XxlJobServiceImpl中注入job.allow.script属性,在添加任务方法中校验任务类型改造如下:
// valid job
if(!GlueTypeEnum.match(jobInfo.getGlueType(),this.allowScript)) {
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_gluetype")+I18nUtil.getString("system_unvalid")) );
}
在GlueTypeEnum中添加重载方法match:
public static boolean match(String name,boolean allowScript){
if(!allowScript) {
return GlueTypeEnum.BEAN.name().equals(name);
}
for (GlueTypeEnum item: GlueTypeEnum.values()) {
if (item.name().equals(name)) {
return true;
}
}
return false;
}
如果不允许脚本任务,则校验任务类型是否是BEAN(只允许BEAN类型),否则和保持原来的逻辑。
这样就在控制台前端入口和后端接口都限制了任务类型,对于禁用脚本任务,可以通过删除GlueTypeEnum中除了BEAN之外的类型结合简单代码调整即可。
前端限制:
接口限制:
我认为对于xxl-job控制台比较安全部署方式是云平台的负载均衡加上容器化部署,以aws为例:
对于accessToken的替换,自己生成一个相对复杂的字符串即可,控制任务类型的创建,在xxl-job-admin的属性配置文件中添加job.allow.script来开启或者关闭脚本任务,这里主要讲下加入google authenticator后的控制台使用方式。
xxl_job_user表添加secret_key字段,然后用如下代码生成google authenticator秘钥:
public static String generatorSecretKey(){
try {
KeyGenerator generator = KeyGenerator.getInstance("HmacSHA1");
int macLengthInBytes = Mac.getInstance("HmacSHA1").getMacLength();
generator.init(macLengthInBytes * 8);
SecretKey secretKey = generator.generateKey();
return new Base32().encodeAsString(secretKey.getEncoded());
}catch (Exception e){
logger.error("生成谷歌验证码失败", e);
return null;
}
}
然后初始化admin账密和secret_key:
INSERT INTO `xxl_job_user`(`id`, `username`, `password`, `role`,
`permission`,`secret_key`) VALUES (1, 'admin',
'e10adc3949ba59abbe56e057f20f883e', 1, NULL,
'QQXUSXJ3SOP7WL4WSI2WA23EALSVTBFO');
这里的secret_key内容是我本机生成,需要自己生成。
绑定google authenticator秘钥有两种方式,一种是按照规范生成二维码:
otpauth://totp/${内容标题}?secret=${秘钥}
#生成
otpauth://totp/xxl-job-admin?secret=QQXUSXJ3SOP7WL4WSI2WA23EALSVTBFO
然后手机安装google authenticator扫描二维码绑定:
这样就绑定成功了,还有一种方式手动绑定,从数据库拿到google authenticator秘钥后手动输入秘钥:
输入自定义账号名称,以及从数据库拿到的密钥进行绑定也能实现相同的效果。
对于管理员创建的非admin账号,google authenticator秘钥是随机生成的,并且非admin用户或者调度中心维护者未必有权限去数据库查看自己的秘钥,基于安全考虑以及评估控制台使用者也基本都是开发人员,并且使用者不不会想业务系统那么大量级,所以对于新增的xxl-job控制台用户,在登陆之前可以向管理员要自己账户的秘钥,自行进行绑定,即可完成后续的登录操作。
前边介绍了改造方式和内容,看官可以基于官方的git项目进行改造,也可以拉取本人改造后的项目进行改造、打包和部署。
https://github.com/ScorpioAeolus/xxl-job
https://www.xuxueli.com/xxl-job/
https://github.com/xuxueli/xxl-job/
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!