公司开发的产品.在用软件扫描漏洞时,扫出了这么一个漏洞.
可以看出有漏洞的地方是登录页面.在登录中,主要逻辑如下: 一些拒绝登录是通过抛异常->然后捕获异常->获取异常信息->跳回到登录页面并展示错误信息. 下面为代码示例
@RequstMapping("login")
public String login(LoginForm loginForm){
try{
myService.doLogin(loginForm);
}catch(Exception e){
request.setAttribute("errorMessage", e.getMessage());//把错误信息提示给用户
return "login";
}
return "index";//成功登录进入主页
}
public void doLogin(LoginForm loginForm)){
if(...){
throw new BusiException("账号被挂起,暂不能登录。");
}
if(...){
throw new BusiException("账号或密码错误");
}
}
感觉异常都可以被正常捕获, 并把错误信息正常提示给用户.
找了很久都不知道是哪里出问题, 看了扫描报告的解决方案: 使用通用页面, 屏蔽掉具体的错误信息. 就开始考虑是不是有的异常没处理好, 导致把异常的英文内容直接返回到页面去了.
最后在看代码的时候发现, 登录的时候涉及到内容的加密和解密. 如果登录时用非常规手段传了一串非法的字符串进行登录.在解密的时候可能会出现一些异常,如索引越界,这样就会把异常的英文内容直接提示给页面了. 以下为解密的代码.
private static byte[] decrypt(byte[] byteArray) {
try {
Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
Security.addProvider(provider);
//Cipher: 提供加密和解密功能的实例
//transformation: "algorithm/mode/padding"
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
PrivateKey privateKey = keyPair.getPrivate();
//初始化
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] plainText = cipher.doFinal(byteArray);
return plainText;
} catch(Exception e) {
throw new MyDecryptException(e); //解密中发生的其他异常,此处没处理,直接抛到上一层
}
}
故在登录接口中处理异常的时候, 要根据不同的异常进行处理. 业务的异常, 如密码错误,账户冻结等, 就按业务异常提示.其他意外的错误, 则统一给提示语, 屏蔽掉异常的英文内容
@RequstMapping("login")
public String login(LoginForm loginForm){
try{
myService.doLogin(loginForm);
}catch(BusiException e){
//业务异常
request.setAttribute("errorMessage", e.getMessage());//把错误信息提示给用户
return "login";
}catch(Exception e){
//其他的意外异常
request.setAttribute("errorMessage", "你是sb吗,乱来?");
return "login";
}
return "index";//成功登录进入主页
}
当然这里也可以改解密方法decrypt(), 捕获异常后给个提示语再抛到上一层, 但是由于在这个应用中decrypt()方法是一个通用方法, 直接抛异常也方便开发在调用这个方法的时候做排查. 估这里选择改Controller中对异常的处理方式
处理后,再次扫描,通过了.
另外,对于这种异常处理, 最好做AOP统一处理, 如ControllerAdvice.
本文到此结束