应用安全扫描
3.4.0版本起,已经新增如下安全问题解决方案:
1. 响应头缺失
- 缺少“Content-Security-Policy”头
- 缺少“X-Content-Type-Options”头
- 缺少“X-XSS-Protection”头
- "Referral Policy" Security 头缺失
解决方法:
nginx.conf在server模块配置:
add_header Content-Security-Policy "default-src 'self'; object-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src * data:; worker-src * blob:; font-src 'self' data:; frame-ancestors 'self';";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1";
add_header 'Referrer-Policy' 'origin';
2. 具有不安全、不正确或缺少 SameSite 属性的 Cookie
http修改为https协议
3. 不安全的Http方法
nginx.conf在server模块配置:
if ($request_method !~* GET|POST) {
return 403;
}
HTTP1.0定义了三种请求方法:GET、POST、HEAD HTTP1.1新增了五种请求方法:OPTIONS、PUT、DELETE、TRACE 、CONNECT 除了GET和POST方法,其他方法都可以视为不安全的方法,根据需求,尽量减少允许的方法
4. 在应用程序中发现不必要的 Http 响应头
响应头中多了:Server: nginx/1.8.0
下载插件(GitHub: https://github.com/openresty/headers-more-nginx-module):
wget https://github.com/openresty/headers-more-nginx-module/archive/v0.33.tar.gz
解压
tar -zxvf v0.33.tar.gz
查看安装参数命令
./nginx/sbin/nginx -V
重新编译nginx,
./configure
后面加--add-module=/app/tools/headers-more-nginx-module-0.33
注意:如果版本不兼容headers-more-nginx-module有部分代码需要修改,根据实际报错代码行修改
最后重启nginx
5. 检测到隐藏目录
对禁止的资源发布“404 - Not Found”响应状态代码,或者将其完全除去
nginx.conf在server模块修改配置(跳转到指定目录下):
## 避免如下配置:
location / {
proxy_pass http://ip:port/;
}
## 应改为:
location / {
proxy_pass http://ip:port/xxxxxx/;
}
## 多模块的话应修改为:
location /xxx1 {
proxy_pass http://ip:port/xxxxxx1/;
}
location /xxx2 {
proxy_pass http://ip:port/xxxxxx2/;
}
6. 跨站点请求伪造
验证“Referer”头的值,并对每个提交的表单使用 one-time-nonce
解决方案:
1、KEMS 3.0.0+ | KOCA 4.0.0+版本,开启CSRF防御,登录成功后,response头会返回一个XSRF-TOKEN,除过滤的url外,所有请求的request head中需要携带X-Xsrf-Token或请求参数中携带csrf_token 才能正常访问。
网关的auth.yml新增配置:
koca: security: # CSRF防御 csrf: enabled: true
2、网关新增Referer拦截器(3.4.0版本已新增)
pom.xml:
<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>9.0.71</version> <scope>compile</scope> </dependency>
auth.yml:
security: enabled: true csrf: excludes:
RefererFilter.java:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @Component @Configuration @WebFilter(filterName = "refererFilter", urlPatterns = "/*") public class RefererFilter implements Filter { public static final Logger logger = LoggerFactory.getLogger(RefererFilter.class); /** * 过滤器配置对象 */ FilterConfig filterConfig = null; /** * 是否启用 */ @Value("${security.enabled}") private boolean enabled; /** * 忽略的URL */ @Value("${security.csrf.excludes}") private String excludes; @Override public void init(FilterConfig filterConfig) { this.filterConfig = filterConfig; } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { logger.error("是否启用Referer 跨站点拦截器" + enabled); HttpServletRequest request = (HttpServletRequest) servletRequest; // 不启用或者已忽略的URL不拦截 if (!enabled || isExcludeUrl(request.getServletPath())) { filterChain.doFilter(servletRequest, servletResponse); return; } String referer = request.getHeader("Referer"); String serverName = request.getServerName(); // 判断是否存在外链请求本站 if (null != referer && !referer.contains(serverName)) { logger.error("Referer过滤器 => 服务器:{} => 当前域名:{}", serverName, referer); servletResponse.setContentType("text/html; charset=utf-8"); servletResponse.getWriter().write("系统不支持当前域名的访问!"); } else { filterChain.doFilter(servletRequest, servletResponse); } } @Override public void destroy() { this.filterConfig = null; } /** * 判断是否为忽略的URL * * @param url URL路径 * @return true-忽略,false-过滤 */ private boolean isExcludeUrl(String url) { if (excludes == null || excludes.isEmpty()) { return false; } List<String> urls = Arrays.asList(excludes.split(",")); return urls.stream().map(pattern -> Pattern.compile("^" + pattern)).map(p -> p.matcher(url)) .anyMatch(Matcher::find); } }
7. 其余因请求参数引发的风险
开启通信加密:
# 前端的public/config.json修改为:
"ENABLE_API_ENCRYPT": true,
# 后端网关bootstrap.yml
koca:
web:
encrypt:
enabled: true #后台是否开启通信加解密,默认false