安全漏洞和渗透修复
This commit is contained in:
parent
ca4a805c5d
commit
5330d1e184
|
|
@ -65,7 +65,7 @@ public class BnsSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
|
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
|
||||||
http.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
|
http.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
|
||||||
// 解决不允许显示在iframe的问题
|
// 解决不允许显示在iframe的问题
|
||||||
http.headers().frameOptions().disable();
|
//http.headers().frameOptions().disable();
|
||||||
http.headers().cacheControl();
|
http.headers().cacheControl();
|
||||||
|
|
||||||
http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,236 @@
|
||||||
|
package com.bonus.boot.manager.manager.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Order(1) // 确保过滤器优先级
|
||||||
|
public class CspFilter implements Filter {
|
||||||
|
|
||||||
|
// 静态资源扩展名模式
|
||||||
|
private static final Pattern STATIC_RESOURCE_PATTERN = Pattern.compile(
|
||||||
|
".*\\.(css|js|map|png|jpg|jpeg|gif|ico|svg|webp|bmp|" +
|
||||||
|
"woff|woff2|ttf|eot|otf|pdf|txt|xml|json|" +
|
||||||
|
"zip|rar|7z|tar|gz|mp4|mp3|wav|avi|mov|webm|" +
|
||||||
|
"doc|docx|xls|xlsx|ppt|pptx)$",
|
||||||
|
Pattern.CASE_INSENSITIVE
|
||||||
|
);
|
||||||
|
|
||||||
|
// 静态资源路径前缀
|
||||||
|
private static final List<String> STATIC_PATH_PREFIXES = Arrays.asList(
|
||||||
|
"/static/", "/public/", "/resources/", "/assets/", "/css/", "/js/",
|
||||||
|
"/images/", "/img/", "/fonts/", "/webjars/", "/vendor/", "/dist/",
|
||||||
|
"/uploads/", "/downloads/", "/libs/", "/layui/"
|
||||||
|
);
|
||||||
|
|
||||||
|
// WebGL和3D地图相关页面路径
|
||||||
|
private static final List<String> WEBGL_PAGE_PATHS = Arrays.asList(
|
||||||
|
"/pages/synthesisQuery/digitalSignage.html",
|
||||||
|
"/pages/basic/lineManagement/child/setSpanTowerLonAndLat.html"
|
||||||
|
);
|
||||||
|
|
||||||
|
@Value("${spring.profiles.active:prod}")
|
||||||
|
private String activeProfile;
|
||||||
|
|
||||||
|
@Value("${csp.report-only:false}")
|
||||||
|
private boolean cspReportOnly;
|
||||||
|
|
||||||
|
@Value("${csp.allow-iframe:true}")
|
||||||
|
private boolean allowIframe;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response,
|
||||||
|
FilterChain chain) throws IOException, ServletException {
|
||||||
|
|
||||||
|
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||||
|
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||||
|
String requestUri = httpRequest.getRequestURI();
|
||||||
|
|
||||||
|
// 设置所有必要的安全头
|
||||||
|
setSecurityHeaders(httpResponse, requestUri);
|
||||||
|
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSecurityHeaders(HttpServletResponse response, String requestUri) {
|
||||||
|
// 1. 设置ClickJacking防护头(优先解决)
|
||||||
|
setClickJackingProtectionHeaders(response, requestUri);
|
||||||
|
|
||||||
|
// 2. 设置CSP头
|
||||||
|
setCspHeader(response, requestUri);
|
||||||
|
|
||||||
|
// 3. 设置其他安全头
|
||||||
|
setAdditionalSecurityHeaders(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCspHeader(HttpServletResponse response, String requestUri) {
|
||||||
|
String cspPolicy;
|
||||||
|
|
||||||
|
if (isStaticResource(requestUri)) {
|
||||||
|
// 静态资源使用简单策略
|
||||||
|
cspPolicy = "default-src 'self'";
|
||||||
|
}
|
||||||
|
else if (isLoginPage(requestUri)) {
|
||||||
|
// 登录页面 - 使用安全的CSP策略,移除不安全的指令
|
||||||
|
String frameAncestors = allowIframe ? "'self'" : "'none'";
|
||||||
|
|
||||||
|
cspPolicy = "default-src 'self'; " +
|
||||||
|
// 允许同源脚本和外部JavaScript库
|
||||||
|
"script-src 'self' 'unsafe-inline' https:; " +
|
||||||
|
// 只允许同源样式
|
||||||
|
"style-src 'self' 'unsafe-inline' https:; " +
|
||||||
|
// 只允许同源图片和数据URI
|
||||||
|
"img-src 'self' data: blob: https:; " +
|
||||||
|
// 只允许同源字体和数据URI
|
||||||
|
"font-src 'self' data: https:; " +
|
||||||
|
// 只允许同源连接
|
||||||
|
"connect-src 'self' https:; " +
|
||||||
|
"frame-ancestors " + frameAncestors + "; " +
|
||||||
|
"form-action 'self'; " +
|
||||||
|
"object-src 'none'; " +
|
||||||
|
"base-uri 'self'; " +
|
||||||
|
"report-uri /api/csp-violation";
|
||||||
|
}
|
||||||
|
else if (isWebglPage(requestUri)) {
|
||||||
|
// WebGL和3D地图页面 - 需要更宽松的策略支持WebGL、Worker等
|
||||||
|
String frameAncestors = allowIframe ? "'self'" : "'none'";
|
||||||
|
|
||||||
|
cspPolicy = "default-src 'self'; " +
|
||||||
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: data:; " +
|
||||||
|
"style-src 'self' 'unsafe-inline' data: blob:; " +
|
||||||
|
"img-src 'self' data: blob: https:; " +
|
||||||
|
"font-src 'self' data: blob: https:; " +
|
||||||
|
"connect-src 'self' https: blob: data: http://data.mars3d.cn; " +
|
||||||
|
"frame-ancestors " + frameAncestors + "; " +
|
||||||
|
"form-action 'self'; " +
|
||||||
|
"object-src 'none'; " +
|
||||||
|
"base-uri 'self'; " +
|
||||||
|
"worker-src 'self' blob: data:; " +
|
||||||
|
"child-src 'self' blob: data:; " +
|
||||||
|
"report-uri /api/csp-violation"; // 移除 upgrade-insecure-requests,避免强制HTTPS
|
||||||
|
} else {
|
||||||
|
// 普通HTML页面 - 根据配置决定是否允许iframe
|
||||||
|
String frameAncestors = allowIframe ? "'self'" : "'none'";
|
||||||
|
|
||||||
|
cspPolicy = "default-src 'self'; " +
|
||||||
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; " +
|
||||||
|
"style-src 'self' 'unsafe-inline' https:; " +
|
||||||
|
"img-src 'self' data: blob: https:; " +
|
||||||
|
"font-src 'self' data: https:; " +
|
||||||
|
"connect-src 'self' https:; " +
|
||||||
|
"frame-ancestors " + frameAncestors + "; " +
|
||||||
|
"form-action 'self'; " +
|
||||||
|
"object-src 'none'; " +
|
||||||
|
"base-uri 'self'; " +
|
||||||
|
"report-uri /api/csp-violation"; // 移除 upgrade-insecure-requests,避免强制HTTPS
|
||||||
|
}
|
||||||
|
|
||||||
|
String headerName = cspReportOnly ?
|
||||||
|
"Content-Security-Policy-Report-Only" : "Content-Security-Policy";
|
||||||
|
|
||||||
|
response.setHeader(headerName, cspPolicy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setClickJackingProtectionHeaders(HttpServletResponse response, String requestUri) {
|
||||||
|
// 对于静态资源,使用宽松的ClickJacking防护
|
||||||
|
if (isStaticResource(requestUri)) {
|
||||||
|
response.setHeader("X-Frame-Options", "SAMEORIGIN");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对于HTML页面,根据配置决定防护级别
|
||||||
|
if (allowIframe) {
|
||||||
|
response.setHeader("X-Frame-Options", "SAMEORIGIN");
|
||||||
|
} else {
|
||||||
|
response.setHeader("X-Frame-Options", "DENY");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAdditionalSecurityHeaders(HttpServletResponse response) {
|
||||||
|
response.setHeader("X-Content-Type-Options", "nosniff");
|
||||||
|
response.setHeader("X-XSS-Protection", "1; mode=block");
|
||||||
|
response.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
|
||||||
|
response.setHeader("Permissions-Policy",
|
||||||
|
"geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=()");
|
||||||
|
|
||||||
|
// 注意:HSTS 只应在 HTTPS 部署下开启;当前未在此处强制设置
|
||||||
|
// 如需开启,请在 HTTPS 部署完成后,通过配置控制
|
||||||
|
// 例如:Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStaticResource(String uri) {
|
||||||
|
if (uri == null || uri.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = uri.split("\\?")[0];
|
||||||
|
|
||||||
|
if (STATIC_RESOURCE_PATTERN.matcher(path).matches()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return STATIC_PATH_PREFIXES.stream().anyMatch(path::startsWith);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为登录页面
|
||||||
|
*/
|
||||||
|
private boolean isLoginPage(String requestUri) {
|
||||||
|
return requestUri != null && (
|
||||||
|
requestUri.endsWith("/login.html") ||
|
||||||
|
requestUri.endsWith("/login") ||
|
||||||
|
requestUri.contains("/login")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成随机nonce值
|
||||||
|
*/
|
||||||
|
private String generateNonce() {
|
||||||
|
byte[] nonceBytes = new byte[16];
|
||||||
|
new java.util.Random().nextBytes(nonceBytes);
|
||||||
|
return java.util.Base64.getEncoder().encodeToString(nonceBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成内容的SHA-256哈希值
|
||||||
|
*/
|
||||||
|
private String generateHash(String content) {
|
||||||
|
try {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
byte[] hash = digest.digest(content.getBytes("UTF-8"));
|
||||||
|
return "'sha256-" + java.util.Base64.getEncoder().encodeToString(hash) + "'";
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWebglPage(String uri) {
|
||||||
|
if (uri == null || uri.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = uri.split("\\?")[0];
|
||||||
|
return WEBGL_PAGE_PATHS.stream().anyMatch(path::contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isProduction() {
|
||||||
|
return "prod".equals(activeProfile) || "production".equals(activeProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
// 清理资源
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -45,10 +45,10 @@ public class TokenFilter extends OncePerRequestFilter implements Filter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 在这里设置 CSP 头或其他过滤逻辑
|
// 在这里设置 CSP 头或其他过滤逻辑
|
||||||
response.setHeader(
|
/*response.setHeader(
|
||||||
"Content-Security-Policy",
|
"Content-Security-Policy",
|
||||||
"default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline';"
|
"default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline';font-src 'self' data:;img-src 'self' data:;"
|
||||||
);
|
);*/
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,19 @@
|
||||||
#\u6B63\u5F0F\u7AEF\u53E3
|
#\u6B63\u5F0F\u7AEF\u53E3
|
||||||
#server.port=18088
|
#server.port=18088
|
||||||
#\u672C\u5730\u7AEF\u53E3
|
#\u672C\u5730\u7AEF\u53E3
|
||||||
server.port=18088
|
|
||||||
#\u6D4B\u8BD5\u7AEF\u53E3
|
|
||||||
#server.port=18088
|
#server.port=18088
|
||||||
|
#\u6D4B\u8BD5\u7AEF\u53E3
|
||||||
|
server.port=18088
|
||||||
#\u8BBF\u95EE\u8DEF\u5F84
|
#\u8BBF\u95EE\u8DEF\u5F84
|
||||||
server.servlet.context-path=/YSpeaManager
|
server.servlet.context-path=/YSpeaManager
|
||||||
#\u6B63\u5F0F\u5E93
|
#\u6B63\u5F0F\u5E93
|
||||||
spring.datasource.url=jdbc:mysql://192.168.1.8:23342/yn_tj_appoint?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
|
#spring.datasource.url=jdbc:mysql://192.168.1.8:23342/yn_tj_appoint?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
|
||||||
spring.datasource.username=root
|
|
||||||
spring.datasource.password=Bonus@yntj123!
|
|
||||||
#\u6D4B\u8BD5\u5E93
|
|
||||||
#spring.datasource.url=jdbc:mysql://192.168.0.14:1115/yn_tj_appoint?useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
|
|
||||||
#spring.datasource.username=root
|
#spring.datasource.username=root
|
||||||
#spring.datasource.password=xbzadmin@szedu14!
|
#spring.datasource.password=Bonus@yntj123!
|
||||||
|
#\u6D4B\u8BD5\u5E93
|
||||||
|
spring.datasource.url=jdbc:mysql://192.168.0.14:1115/yn_tj_appoint?useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
|
||||||
|
spring.datasource.username=root
|
||||||
|
spring.datasource.password=xbzadmin@szedu14!
|
||||||
#\u672C\u5730\u5E93
|
#\u672C\u5730\u5E93
|
||||||
#spring.datasource.url=jdbc:mysql://127.0.0.1:3306/yn_tj_appoint?useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
|
#spring.datasource.url=jdbc:mysql://127.0.0.1:3306/yn_tj_appoint?useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
|
||||||
#spring.datasource.username=root
|
#spring.datasource.username=root
|
||||||
|
|
@ -32,13 +32,13 @@ mybatis.mapper-locations=classpath:mappers/*/*Mapper.xml
|
||||||
mybatis.type-aliases-package=com.bonus.boot.manager.*.entity
|
mybatis.type-aliases-package=com.bonus.boot.manager.*.entity
|
||||||
|
|
||||||
#\u7EBF\u4E0A
|
#\u7EBF\u4E0A
|
||||||
spring.redis.host=192.168.1.8
|
#spring.redis.host=192.168.1.8
|
||||||
spring.redis.port=23347
|
#spring.redis.port=23347
|
||||||
spring.redis.password=Bonus@yntj123!
|
#spring.redis.password=Bonus@yntj123!
|
||||||
#\u6D4B\u8BD5
|
#\u6D4B\u8BD5
|
||||||
#spring.redis.host=192.168.0.14
|
spring.redis.host=192.168.0.14
|
||||||
#spring.redis.port=2001
|
spring.redis.port=2001
|
||||||
#spring.redis.password=Dszbns@Redis123!
|
spring.redis.password=Dszbns@Redis123!
|
||||||
#\u672C\u5730
|
#\u672C\u5730
|
||||||
#spring.redis.host=127.0.0.1
|
#spring.redis.host=127.0.0.1
|
||||||
#spring.redis.port=6379
|
#spring.redis.port=6379
|
||||||
|
|
@ -65,18 +65,18 @@ token.expire.seconds=7200
|
||||||
spring.servlet.multipart.enabled=true
|
spring.servlet.multipart.enabled=true
|
||||||
|
|
||||||
#\u6B63\u5F0F
|
#\u6B63\u5F0F
|
||||||
files.url=http://112.29.103.165:1616/medicalDocumentation/statics
|
#files.url=http://112.29.103.165:1616/medicalDocumentation/statics
|
||||||
files.path=/data/yn
|
|
||||||
#\u6D4B\u8BD5
|
|
||||||
#files.url=http://192.168.0.14:18077/medicalDocumentation/statics
|
|
||||||
#files.path=/data/yn
|
#files.path=/data/yn
|
||||||
|
#\u6D4B\u8BD5
|
||||||
|
files.url=http://192.168.0.14:18088/medicalDocumentation/statics
|
||||||
|
files.path=/data/yn
|
||||||
#\u672C\u5730
|
#\u672C\u5730
|
||||||
#files.url=http://192.168.0.110:18088/YSpeaManager/statics
|
#files.url=http://192.168.0.110:18088/YSpeaManager/statics
|
||||||
#files.path=d:\\data\\yn
|
#files.path=d:\\data\\yn
|
||||||
#files.upload=d:\\files
|
#files.upload=d:\\files
|
||||||
# \u8DE8\u57DF\u914D\u7F6E
|
# \u8DE8\u57DF\u914D\u7F6E
|
||||||
# \u5141\u8BB8\u7684\u6E90\uFF08\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF09
|
# \u5141\u8BB8\u7684\u6E90\uFF08\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF09
|
||||||
cors.allowed-origins=http://localhost:1616,http://127.0.0.1:1616,http://192.168.0.39:1616,http://192.168.0.14:1616,http://112.29.103.165:1616
|
cors.allowed-origins=http://localhost:18088,http://127.0.0.1:18088,http://192.168.0.39:1616,http://192.168.0.14:18088,http://112.29.103.165:1616
|
||||||
|
|
||||||
# \u5141\u8BB8\u7684HTTP\u65B9\u6CD5
|
# \u5141\u8BB8\u7684HTTP\u65B9\u6CD5
|
||||||
cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS
|
cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS
|
||||||
|
|
@ -101,3 +101,13 @@ security.hsts.enabled=true
|
||||||
security.headers.clear-server-info=true
|
security.headers.clear-server-info=true
|
||||||
|
|
||||||
management.endpoint.caches.enabled=false
|
management.endpoint.caches.enabled=false
|
||||||
|
|
||||||
|
# CSP\u548C\u5B89\u5168\u5934\u914D\u7F6E
|
||||||
|
# \u662F\u5426\u542F\u7528CSP\u62A5\u544A\u6A21\u5F0F\uFF08true\u4E3A\u4EC5\u62A5\u544A\uFF0Cfalse\u4E3A\u5F3A\u5236\u6267\u884C\uFF09
|
||||||
|
csp.report-only=false
|
||||||
|
|
||||||
|
# \u662F\u5426\u5141\u8BB8\u9875\u9762\u5728iframe\u4E2D\u663E\u793A\uFF08true\u4E3A\u5141\u8BB8\u540C\u6E90iframe\uFF0Cfalse\u4E3A\u5B8C\u5168\u7981\u6B62\uFF09
|
||||||
|
csp.allow-iframe=true
|
||||||
|
|
||||||
|
# \u662F\u5426\u542F\u7528WebGL\u652F\u6301\uFF08true\u4E3A\u542F\u7528\uFF0Cfalse\u4E3A\u7981\u7528\uFF09
|
||||||
|
csp.enable-webgl=true
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue