扫描漏洞
下载AppScan
https://bigcy.jb51.net/202205/tools/HCLAppScanStandardpjb_jb51.rar
安装
首先执行 Appscan_Setup_10071.exe 安装软件, 选择自己需要安装的语言, 这里选择 中文(简体)。
选择安装目录,可以使用默认目录,也可以点击更改自定义安装目录,选择完成后点击安装
安装完成后将 nGen_Crack 目录下的两个 dll 文件替换到安装目录下, 启动应用, 点击 帮助->许可证 可以看到到期日期为永久, 至此,AppScan安装完成。
使用
创建 Web应用程序, 文件->新建->Web应用程序扫描, 填写起始URL,下方显示绿色字体已连接到服务器表示填写的URL有效 ,点击下一步配置登录策略。
如果没有特殊要求的话,我们登录方法选择无,当然这个按需设置就好,并不是说都得选择无,我们选择登录:
登录后点“我已登录到站点”
测试策略和测试优化项如无特殊要求的话,我们都选择默认值。
配置完成,选择启动扫描,点击完成开始漏洞扫描。
扫描完成后,可以看到问题分类,如下所示
有需要的话可以创建扫描报告,里面会有详细的问题分类及解决建议(仅供参考)
处理漏洞
API成批分配
package com.daasan.modules.sys.interceptor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
/**
**序列化和反序列化处理漏洞:API成批分配
* @create 2024/6/6 15:50
**/
@Configuration
public class JacksonConverters {
@Bean
public HttpMessageConverters customConverters() {
// 创建一个ObjectMapper对象,用于配置JSON的序列化和反序列化
ObjectMapper objectMapper = new ObjectMapper();
// 配置ObjectMapper,使其在反序列化时遇到未知属性时抛出异常
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
// 创建一个MappingJackson2HttpMessageConverter对象,用于处理HTTP请求和响应中的JSON数据
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
// 将配置好的ObjectMapper设置到MappingJackson2HttpMessageConverter中
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
// 创建一个HttpMessageConverters对象,并传入我们自定义的MappingJackson2HttpMessageConverter
// HttpMessageConverters是一个集合,可以包含多个HttpMessageConverter,但这里我们只添加了一个
HttpMessageConverters converters = new HttpMessageConverters(mappingJackson2HttpMessageConverter);
// 返回配置好的HttpMessageConverters对象,Spring框架在需要处理HTTP消息时会使用它
return converters;
}
}
恶意参数注入
增加拦截器拦截恶意请求参数
import com.demo.common.core.exception.BsException;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
**校验参数中是否包含 命令执行、sql注入、恶意编码 等危险内容的拦截器
* @create 2024/6/4 14:11
**/
@Component
public class ParameterInjectionInterceptor implements HandlerInterceptor, Ordered {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
String[] parameterValues = request.getParameterValues(parameterName);
for (String parameterValue : parameterValues) {
if (containsDangerousContent(parameterValue)) {
System.out.println("拦截到危险内容。。。。。。。。。。。。。。。");
response.sendError(403);
return false; // 停止后续拦截器和处理器执行
}
}
}
return true; // 继续执行后续拦截器和处理器
}
/**
**使用正则表达式来检测危险内容
* @param input
* @return boolean
* @author yinqi
* @create 2024/6/4 14:16
**/
private boolean containsDangerousContent(String input) {
ArrayList<String> patterns = new ArrayList<>();
// 命令执行尝试(如Unix shell命令)
patterns.add("(\\b(cmd|sh|bash|powershell|eval|exec|system)\\b)|([;>&|])");
// SQL注入尝试(如常见的SQL关键字)
patterns.add("(select|insert|update|delete|drop|truncate|exec|declare|char|or |and)");
// 跨站脚本(XSS)尝试(如常见的JavaScript关键字和HTML标签)
patterns.add("<script\\b|\\balert\\b|on(load|error|mouseover|click|mousemove|mouseout|keydown|keypress|keyup|dblclick|click|blur|focus|contextmenu|dragstart|dragenter|dragover|dragleave|drag|drop|dragend)|javascript:|livescript:|vbscript:|mocha:|charset=|window\\.|\\.innerHTML|<iframe|<frameset|<frame|<script|<xss|data:text\\/html");
// 可能的路径遍历尝试(如../)
patterns.add("(\\.\\./|\\.\\.)");
// 可能的编码绕过尝试(如%00、%25等)
patterns.add("(%[0-9a-fA-F]{2})+");
// 使用Pattern和Matcher类来检查输入字符串是否匹配任何危险模式
for (String pattern : patterns) {
Pattern p = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(input);
if (m.find()) {
System.out.println("危险内容:"+input);
throw new BsException("危险内容:"+input);
}
}
return false; // 没有发现危险内容
}
// 实现Ordered接口以设置顺序
@Override
public int getOrder() {
return 1; // 较低的数字表示较高的优先级
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private ParameterInjectionInterceptor firstInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(firstInterceptor)
.addPathPatterns("/**"); // 设置拦截路径
}
}
API 功能级授权中断
请求的接口返回了系统异常的错误提示且定义的状态为400,但实际上返回给浏览器的状态为200导致HCL appscan扫描工具误以为成功访问了应用程序/API ,所以记录了该漏洞。
处理办法:返回给浏览器的状态也要改为400:
import com.demo.common.core.api.R;
import com.demo.common.core.exception.BsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.io.IOException;
import java.util.regex.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 全局拦截异常处理器
* @date 2024/3/7
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 定义正则表达式,匹配所有连续的汉字
*/
Pattern pattern = Pattern.compile("[\\u4e00-\\u9fa5]+");
/**
* 拦截抛出的 bsException 异常
* @param bsException
* @param request
* @return
*/
@ExceptionHandler(BsException.class)
public R<?> bsException(BsException bsException, HttpServletRequest request, HttpServletResponse response) {
String requestURI = request.getRequestURI();
bsException.printStackTrace();
log.error("请求地址'{}',发生BsException异常:'{}'",requestURI,bsException.getMessage());
// TODO 匹配 bsException.getMessage() 是否为汉字,如果是,那么返回汉字。不是汉字返回 系统异常
String res = bsException.getMessage();
Matcher matcher = pattern.matcher(res);
if( !matcher.find() ) {
res = "系统异常";
}
try {
response.sendError(400);
} catch (IOException e) {
e.printStackTrace();
}
return R.failure(res);
}
/**
* 拦截抛出的 runtimeException 异常
* @param runtimeException
* @param request
* @return
*/
@ExceptionHandler(RuntimeException.class)
public R<?> runtimeException(RuntimeException runtimeException, HttpServletRequest request, HttpServletResponse response) {
String requestURI = request.getRequestURI();
runtimeException.printStackTrace();
log.error("请求地址'{}',发生RuntimeException异常:'{}'",requestURI,runtimeException.getMessage());
try {
response.sendError(400);
} catch (IOException e) {
e.printStackTrace();
}
return R.failure("系统异常");
}
/**
* 兜底方法,拦截抛出的所有异常
* @param exception
* @param request
* @return
*/
@ExceptionHandler(Exception.class)
public R<?> exception(Exception exception, HttpServletRequest request, HttpServletResponse response) {
String requestURI = request.getRequestURI();
exception.printStackTrace();
log.error("请求地址'{}',拦截到兜底异常:'{}'",requestURI,exception.getMessage());
try {
response.sendError(400);
} catch (IOException e) {
e.printStackTrace();
}
return R.failure("系统异常");
}
}
前端页面漏洞1
访问不存在的路径时,系统跳转到了首页,但是给浏览器返回的状态是200,导致扫描工具误以为成功访问了应用程序/API 而记录了漏洞。
解决办法:nginx.conf配置文件中的server--> location /{}中注释“try_files $uri $uri/ /index.html;”,访问错误时不跳转到index.html页面。
location / {
root html;
index index.html;
# 用于配合 browserHistory 使用
#try_files $uri $uri/ /index.html;
}
前端页面漏洞二
访问一个不存在的静态js文件,系统跳转到了首页,但是给浏览器返回的状态是200,导致扫描工具误以为成功访问了应用程序/API 而记录了漏洞。
解决办法:nginx.conf配置文件中的server中增加配置,访问的静态资源不存在时返回404
#访问的静态资源不存在时返回404
location ~* \.(?:gif|jpg|jpeg|png|ico|css|js|woff|woff2|ttf|otf|eot|svg|mp4|webm|wav|mp3|m4a|aac|oga)$ {
try_files $uri =404; # 如果文件不存在,直接返回404
}
error_page 404 /404.html; # 如果需要自定义404页面,请确保404.html文件存在