XSS攻击防护增强,ISSUS-3964类似问题批量解决,安全性提升;

3.4.1-release
xuxueli 3 days ago
parent 7271fc7152
commit b314e4ac76

@ -2851,13 +2851,16 @@ alter table xxl_job_log
- 5、【升级】调度中心UI交互优化任务及日志列表下拉框支持模糊搜索提升交互体验
- 6、【修复】XxlJobFileAppender自定义地址callbackLogPath设置无效问题修复合并ISSUS-3963
- 7、【优化】调度组件守护线程代码重构提升稳定性以及可维护性
- 8、【修复】XSS攻击防护增强ISSUS-3964类似问题批量解决安全性提升
### 7.46 版本 v3.5.0 Release Notes[ING]
- 1、【TODO】AccessToken线上化管理执行期维度限制操作当前执行期
- 2、【TODO】调度中心OpenAPI增强提供任务管理能力封装Agent Skill并推送ClawHub
- 3、【TODO】配置线上化发送邮箱配置线上管理、线程池配置调整
- 4、【TODO】任务告警支持Webhook、邮箱多种方式
- 4、【TODO】任务告警拆分“告警类型、告警配置”属性支持Webhook、邮箱多种方式
- 5、【TODO】任务说明拆分“任务名称、任务描述”属性前者用于任务检索后者用于补充任务描述如参数说明、功能详细介绍等。
- 6、【TODO】GLUE模式开关执行器新增GLUE模式配置支持任务维度启停满足差异化场景需求
### TODO LIST

@ -6,6 +6,7 @@ import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobLogGlue;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.admin.framework.util.JobGroupPermissionUtil;
import com.xxl.job.admin.framework.util.XssUtil;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.core.StringTool;
@ -79,6 +80,9 @@ public class JobCodeController {
if (glueRemark.length()<4 || glueRemark.length()>100) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_remark_limit"));
}
if (XssUtil.hasXss(glueRemark)) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_remark") + I18nUtil.getString("system_invalid"));
}
XxlJobInfo existsJobInfo = xxlJobInfoMapper.loadById(id);
if (existsJobInfo == null) {
return Response.ofFail( I18nUtil.getString("jobinfo_glue_jobid_invalid"));

@ -7,6 +7,7 @@ import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.admin.business.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.business.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.business.mapper.XxlJobRegistryMapper;
import com.xxl.job.admin.framework.util.XssUtil;
import com.xxl.job.core.constant.Const;
import com.xxl.job.core.constant.RegistTypeEnum;
import com.xxl.sso.core.annotation.XxlSso;
@ -71,27 +72,31 @@ public class JobGroupController {
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<String> insert(XxlJobGroup xxlJobGroup){
// valid
// valid appname
if (StringTool.isBlank(xxlJobGroup.getAppname())) {
return Response.ofFail((I18nUtil.getString("system_please_input")+"AppName") );
}
if (xxlJobGroup.getAppname().length()<4 || xxlJobGroup.getAppname().length()>64) {
return Response.ofFail( I18nUtil.getString("jobgroup_field_appname_length") );
}
if (xxlJobGroup.getAppname().contains(">") || xxlJobGroup.getAppname().contains("<")) {
if (XssUtil.hasXss(xxlJobGroup.getAppname())) {
return Response.ofFail( "AppName"+I18nUtil.getString("system_invalid") );
}
// valid title
if (StringTool.isBlank(xxlJobGroup.getTitle())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
}
if (xxlJobGroup.getTitle().contains(">") || xxlJobGroup.getTitle().contains("<")) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_title")+I18nUtil.getString("system_invalid") );
if (XssUtil.hasXss(xxlJobGroup.getTitle())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_title") + I18nUtil.getString("system_invalid"));
}
if (xxlJobGroup.getAddressType()!=0) {
if (xxlJobGroup.getAddressType() != 0) {
// valid addressList
if (StringTool.isBlank(xxlJobGroup.getAddressList())) {
return Response.ofFail( I18nUtil.getString("jobgroup_field_addressType_limit") );
}
if (xxlJobGroup.getAddressList().contains(">") || xxlJobGroup.getAddressList().contains("<")) {
if (XssUtil.hasXss(xxlJobGroup.getAddressList())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_registryList")+I18nUtil.getString("system_invalid") );
}
@ -117,16 +122,26 @@ public class JobGroupController {
@ResponseBody
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<String> update(XxlJobGroup xxlJobGroup){
// valid
// valid appname
if (StringTool.isBlank(xxlJobGroup.getAppname())) {
return Response.ofFail((I18nUtil.getString("system_please_input")+"AppName") );
}
if (xxlJobGroup.getAppname().length()<4 || xxlJobGroup.getAppname().length()>64) {
return Response.ofFail( I18nUtil.getString("jobgroup_field_appname_length") );
}
if (XssUtil.hasXss(xxlJobGroup.getAppname())) {
return Response.ofFail( "AppName"+I18nUtil.getString("system_invalid") );
}
// valid title
if (StringTool.isBlank(xxlJobGroup.getTitle())) {
return Response.ofFail( (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
}
if (XssUtil.hasXss(xxlJobGroup.getTitle())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_title") + I18nUtil.getString("system_invalid"));
}
if (xxlJobGroup.getAddressType() == 0) {
// 0=自动注册
List<String> registryList = findRegistryByAppName(xxlJobGroup.getAppname());
@ -138,9 +153,15 @@ public class JobGroupController {
xxlJobGroup.setAddressList(addressListStr);
} else {
// 1=手动录入
// valid addressList
if (StringTool.isBlank(xxlJobGroup.getAddressList())) {
return Response.ofFail( I18nUtil.getString("jobgroup_field_addressType_limit") );
}
if (XssUtil.hasXss(xxlJobGroup.getAddressList())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_registryList")+I18nUtil.getString("system_invalid") );
}
String[] addresss = xxlJobGroup.getAddressList().split(",");
for (String item: addresss) {
if (StringTool.isBlank(item)) {

@ -0,0 +1,102 @@
package com.xxl.job.admin.framework.util;
import java.util.regex.Pattern;
public class XssUtil {
/**
* XSS
*
* 1. <script>
* 2. javascript: vbscript:
* 3. (onload, onclick, onerror )
* 4. eval(), expression()
*/
private static final Pattern[] PATTERNS = {
Pattern.compile("<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL),
Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
Pattern.compile("on(load|error|click|mouseover|focus|blur|submit|change|input)\\s*=", Pattern.CASE_INSENSITIVE),
Pattern.compile("eval\\s*\\(", Pattern.CASE_INSENSITIVE),
Pattern.compile("expression\\s*\\(", Pattern.CASE_INSENSITIVE),
Pattern.compile("url\\s*\\(", Pattern.CASE_INSENSITIVE),
Pattern.compile("<iframe[^>]*>", Pattern.CASE_INSENSITIVE),
Pattern.compile("<object[^>]*>", Pattern.CASE_INSENSITIVE),
Pattern.compile("<embed[^>]*>", Pattern.CASE_INSENSITIVE)
};
/**
* check XSS attack patterns in a string
*
* @param value original string
* @return true if XSS attack detected, false otherwise
*/
public static boolean hasXss(String value) {
if (value == null || value.isEmpty()) {
return false;
}
// 遍历所有正则模式进行匹配
for (Pattern pattern : PATTERNS) {
if (pattern.matcher(value).find()) {
return true;
}
}
return false;
}
/**
* XSS
*
* @param value original string
* @return cleaned string
*/
public static String cleanXss(String value) {
if (value == null || value.isEmpty()) {
return value;
}
String result = value;
// 1. 移除危险标签和脚本
for (Pattern pattern : PATTERNS) {
result = pattern.matcher(result).replaceAll("");
}
// 2. 特殊字符 HTML 实体转义
result = escapeHtml(result);
return result;
}
/**
* HTML
*/
private static String escapeHtml(String input) {
if (input == null) {
return null;
}
return input.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;")
.replace("'", "&#x27;");
}
/*public static void main(String[] args) {
String safeInput = "Hello World";
String xssInput1 = "<script>alert('XSS')</script>";
String xssInput2 = "<img src=x onerror=alert(1)>";
String xssInput3 = "javascript:alert(1)";
System.out.println("Safe Input Has XSS: " + hasXss(safeInput)); // false
System.out.println("XSS Input 1 Has XSS: " + hasXss(xssInput1)); // true
System.out.println("XSS Input 2 Has XSS: " + hasXss(xssInput2)); // true
System.out.println("XSS Input 3 Has XSS: " + hasXss(xssInput3)); // true
System.out.println("Cleaned XSS 1: " + cleanXss(xssInput1)); // alert('XSS') (script tags removed, content escaped if needed)
System.out.println("Cleaned XSS 2: " + cleanXss(xssInput2)); // (img tag and event handler removed)
}*/
}

@ -37,6 +37,9 @@ public class XxlJobExecutor {
private static XxlJobExecutor xxlJobExecutor = null;
public static XxlJobExecutor getInstance() {
if (xxlJobExecutor == null) {
throw new RuntimeException(">>>>>>>>>>> xxl-job load executor instance fail, please initialize it.");
}
return xxlJobExecutor;
}
@ -367,7 +370,7 @@ public class XxlJobExecutor {
public JobThread registJobThread(int jobId, IJobHandler handler, String removeOldReason){
JobThread newJobThread = new JobThread(jobId, handler);
newJobThread.start();
logger.info(">>>>>>>>>>> xxl-job regist JobThread success, jobId:{}, handler:{}", new Object[]{jobId, handler});
logger.info(">>>>>>>>>>> xxl-job register JobThread success, jobId:{}, handler:{}", new Object[]{jobId, handler});
JobThread oldJobThread = jobThreadRepository.put(jobId, newJobThread); // putIfAbsent | oh my god, map's put method return the old value!!!
if (oldJobThread != null) {

Loading…
Cancel
Save