其他
一行代码引来的安全漏洞,就让我们丢失了整个服务器的控制权
The following article is from 程序猿石头 Author 码农唐磊
背景说明
中文用户看到的提示就是“您输入的验证码已过期,请重新获取”; 欧美用户看到的效果是“The verification code you input is expired, ...”; 德国用户看到的是:“Der von Ihnen eingegebene Verifizierungscode ist abgelaufen, bitte wiederholen” 。(我瞎找的翻译,不一定准 ) ……
public class GlobalExceptionHandler {
@ExceptionHandler(BadRequestException.class)
@ResponseBody
public ResponseEntity handle(HttpServletRequest request, BadRequestException e){
String i18message = getI18nMessage(e.getKey(), request);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Response.error(e.getCode(), i18message));
}
@ExceptionHandler(ErrorCodeException.class)
@ResponseBody
public ResponseEntity handle(HttpServletRequest request, ErrorCodeException e){
String i18message = getI18nMessage(e.getKey(), request);
return ResponseEntity.status(HttpStatus.OK).body(Response.error(e.getCode(), i18message));
}
}
try {
return messageSource.getMessage(key, null, LanguaggeUtils.currentLocale(request));
} catch (Exception e) {
// log
return key;
}
}
private String nickname;
private String gender;
private String email;
}
昵称字段:“nickname” 必填,长度必须是 6 到 20 位;
性别字段:“gender” 可选,如果填了,就必须是“Male/Female/Other/”中的一种。(说啥,除了男女还有其他?对,是的。毕竟全球用户嘛,你去看看非死不可,还有更多。
) 邮箱:“email”,必填,必须满足邮箱格式。
@Length(min = 6, max = 20, message = "validate.userRegForm.nickname")
private String nickname;
@Gender(message="validate.userRegForm.gender")
private String gender;
@NotNull
@Email(message="validate.userRegForm.email")
private String email;
}
@Gender
就是一个自定义的注解。CustomParam
:@Constraint(validatedBy = CustomValidator.class)
@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomParam {
String message() default "name.tanglei.www.validator.CustomArray.defaultMessage";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default { };
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
@interface List {
CustomParam[] value();
}
}
CustomValidator
:@Override
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
if (null == s || s.isEmpty()) {
return true;
}
if (s.equals("tanglei")) {
return true;
} else {
error(constraintValidatorContext, "Invalid params: " + s);
return false;
}
}
@Override
public void initialize(CustomParam constraintAnnotation) {
}
private static void error(ConstraintValidatorContext context, String message) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message).addConstraintViolation();
}
}
一切都显得很完美,直到上线前代码提交至安全团队扫描,就被“啪啪打脸”,扫描报告反馈了一个严重的安全漏洞。而这个安全漏洞,属于很高危的远程代码执行漏洞。
"message": "Invalid params: 1+1=2"
。* @param messageTemplate new un-interpolated constraint message
* @return returns a constraint violation builder
*/
ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate);
org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator.interpolateMessage
。以为这样就完了吗?
漏洞根因
用户权限分离:运行程序的用户不应该用 root,例如新建一个“web”或者“www”之类的用户,并设置该用户的权限,比如不能有可执行 xx 的权限之类的。本文 case,如果权限进行了分离(遵循最小权限原则),应该也不会这么严重。(本文就刚好是因为是测试环境,所以没有强制实施) 任何时候都不要相信用户的输入,必须对用户输入的进行校验和过滤,又特别是针对公网上的应用。 敏感信息加密保存。退一万步讲,假设攻击者攻入了你的服务器,如果这个时候,你的数据库账户信息等配置都直接明文保存在服务器中。那数据库也被脱走了。