mirror of
https://github.com/alibaba/canal.git
synced 2026-11-04 00:30:15 +08:00
fix: mitigate SnakeYAML deserialization RCE and enforce default password change
1. Use SafeConstructor in ApplicationConfigMonitor to block arbitrary object instantiation via YAML type tags (e.g. !!javax.script.ScriptEngineManager). 2. Detect default admin password (admin/123456) on login and enforce password change before allowing any other API operations (code 50015). 🤖 Generated with [Qoder][https://qoder.com]
This commit is contained in:
@@ -98,6 +98,26 @@ public class WebConfig implements WebMvcConfigurer {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否使用默认密码,如果是则仅允许改密和登出操作
|
||||
User validUser = (User) httpServletRequest.getAttribute("user");
|
||||
if (validUser != null && validUser.isDefaultPassword()) {
|
||||
String uri = httpServletRequest.getRequestURI();
|
||||
String method = httpServletRequest.getMethod();
|
||||
boolean isPasswordChange = "PUT".equals(method) && uri.matches("/api/[^/]+/user");
|
||||
boolean isLogout = uri.contains("/user/logout");
|
||||
if (!isPasswordChange && !isLogout) {
|
||||
BaseModel baseModel = BaseModel.getInstance(null);
|
||||
baseModel.setCode(50015);
|
||||
baseModel.setMessage("Default password must be changed before proceeding");
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String json = mapper.writeValueAsString(baseModel);
|
||||
httpServletResponse.setContentType("application/json;charset=UTF-8");
|
||||
PrintWriter out = httpServletResponse.getWriter();
|
||||
out.print(json);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
})
|
||||
|
||||
@@ -56,6 +56,9 @@ public class UserController {
|
||||
String token = UUID.randomUUID().toString();
|
||||
loginUsers.put(token, loginUser);
|
||||
tokenResp.put("token", token);
|
||||
if (loginUser.isDefaultPassword()) {
|
||||
tokenResp.put("needPasswordChange", "true");
|
||||
}
|
||||
return BaseModel.getInstance(tokenResp);
|
||||
} else {
|
||||
BaseModel<Map<String, String>> model = BaseModel.getInstance(null);
|
||||
@@ -98,6 +101,7 @@ public class UserController {
|
||||
HttpServletRequest httpServletRequest) {
|
||||
userService.update(user);
|
||||
String token = (String) httpServletRequest.getAttribute("token");
|
||||
user.setDefaultPassword(false);
|
||||
loginUsers.put(token, user);
|
||||
return BaseModel.getInstance("success");
|
||||
}
|
||||
|
||||
@@ -44,6 +44,9 @@ public class User extends Model {
|
||||
@Transient
|
||||
private String oldPassword;
|
||||
|
||||
@Transient
|
||||
private boolean defaultPassword;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -115,4 +118,12 @@ public class User extends Model {
|
||||
public void setOldPassword(String oldPassword) {
|
||||
this.oldPassword = oldPassword;
|
||||
}
|
||||
|
||||
public boolean isDefaultPassword() {
|
||||
return defaultPassword;
|
||||
}
|
||||
|
||||
public void setDefaultPassword(boolean defaultPassword) {
|
||||
this.defaultPassword = defaultPassword;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.alibaba.otter.canal.admin.service.impl;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
|
||||
@@ -19,7 +21,11 @@ import com.alibaba.otter.canal.protocol.SecurityUtil;
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
private static byte[] seeds = "canal is best!".getBytes();
|
||||
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
|
||||
|
||||
private static final String DEFAULT_PASSWORD_HASH = "6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9";
|
||||
|
||||
private static byte[] seeds = "canal is best!".getBytes();
|
||||
|
||||
private static final Integer PASSWORD_LENGTH = 5;
|
||||
|
||||
@@ -40,6 +46,11 @@ public class UserServiceImpl implements UserService {
|
||||
throw new ServiceException("user:" + user.getName() + " auth failed!");
|
||||
}
|
||||
|
||||
if (DEFAULT_PASSWORD_HASH.equalsIgnoreCase(user.getPassword())) {
|
||||
user.setDefaultPassword(true);
|
||||
logger.warn("Default admin password detected for user: {}. Please change immediately.", username);
|
||||
}
|
||||
|
||||
user.setPassword("");
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.context.refresh.ContextRefresher;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
|
||||
import com.alibaba.otter.canal.adapter.launcher.loader.CanalAdapterService;
|
||||
import com.alibaba.otter.canal.client.adapter.support.Util;
|
||||
@@ -70,7 +72,7 @@ public class ApplicationConfigMonitor {
|
||||
super.onFileChange(file);
|
||||
try {
|
||||
// 检查yml格式
|
||||
new Yaml().loadAs(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8), Map.class);
|
||||
new Yaml(new SafeConstructor(new LoaderOptions())).loadAs(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8), Map.class);
|
||||
|
||||
canalAdapterService.destroy();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user