漏洞说明:通过篡改请求头中的Referer值依旧能够访问到接口。
通过http请求头里面的Referer随意访问接口
通过下面两个代码类程序来实现你的程序不会被攻击,里面有两个实体,如果你感觉这个程序对你有用,联系我,我私发你,代码就不做过多的解释,原理不难
package com.datalook.manage.filter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @description:
* @author: guoyunlong
* @create: 2023-11-23 13:08
**/
@Configuration
public class CsrfFilterConfig implements WebMvcConfigurer{
@Bean
public CsrfFilterInterceptor myCsrfInterceptor(){
return new CsrfFilterInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//这里可以添加多个拦截器
registry.addInterceptor(myCsrfInterceptor())
.addPathPatterns("/**")
//img和resources为静态资源
.excludePathPatterns("/img/**")
.excludePathPatterns("/images/**")
.excludePathPatterns("/resources/**")
.excludePathPatterns("/actuator/**")
.excludePathPatterns("/actuator/prometheus/**")
.excludePathPatterns("/permission/**");
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/login")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.maxAge(3600);
}
}
package com.datalook.manage.filter;
import com.datalook.util.common.ComponentProperties;
import com.datalook.util.common.StringUtils;
import com.datalook.util.log.LogUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @description:
* @author: guoyunlong
* @create: 2023-11-23 13:08
**/
@Component
public class CsrfFilterInterceptor implements HandlerInterceptor {
@Autowired
private ComponentProperties security;
public boolean preHandle(HttpServletRequest request, HttpServletResponse httpServletResponse, Object o) throws Exception {
String referer = request.getHeader("Referer");
String serverName = request.getServerName();
//如果 referer 为空放行
if (!StringUtils.hasText(referer)) {
return true;
}
URL url = null;
try {
url = new URL(referer);
} catch (MalformedURLException e) {
httpServletResponse.setContentType("application/json; charset=UTF-8");
httpServletResponse.getWriter().write("系统不支持当前域名的访问!");
LogUtil.error("域名:{}解析异常", url);
}
referer = url.getHost();
if (StringUtils.hasText(referer)) {
// 不启用或者已忽略的domain不拦截
if (!security.getCsrf().isEnable() || isExcludesDomain(referer)) {
return true;
}
}
// 判断是否存在外链请求本站
if (StringUtils.hasText(referer) && referer.indexOf(serverName) < 0) {
LogUtil.error("拦截到非法请求:=> 服务器域名:{} => 非法访问域名:{}", serverName, referer);
httpServletResponse.setContentType("application/json; charset=UTF-8");
httpServletResponse.getWriter().write("系统不支持当前域名的访问!");
return false;
} else {
return true;
}
}
/**
* 判断是否为忽略的域名
*
* @param urlPath URL路径
* @return true-忽略,false-过滤
*/
private boolean isExcludesDomain(String urlPath) {
if (security.getCsrf().getExcludesDomain() == null || security.getCsrf().getExcludesDomain().isEmpty()) {
return false;
}
return security.getCsrf().getExcludesDomain().stream().map(pattern -> Pattern.compile("^" + pattern)).map(p -> p.matcher(urlPath))
.anyMatch(Matcher::find);
}
}
使用postman来测试修复后的结果,如果别人使用随意一个域名是可以访问你的接口或者页面的