Springboot集成CSRF防攻擊過程
Springboot集成CSRF防攻擊
CSRF 就是跨域請求偽造,是一種常見的web攻擊方式,解決思路也非常簡單,主要是設(shè)置域名或路徑白名單,對于未知的鏈接予以過濾,從而達(dá)到防護(hù)目的。
總共兩個類:
- 一個CSRFFilterConfigUtils防護(hù)配置工具類,主要作用是配置防護(hù)開關(guān)、請求路徑白名單以及請求域名白名單;
- 一個是CsrfFilter防護(hù)過濾類,該類實(shí)質(zhì)是一個攔截器,攔截所有用戶請求,匹配路徑和域名,符合條件的通過,不符合條件的攔截掉;
以下為實(shí)際代碼:
CSRFFilterConfigUtils 防護(hù)配置工具類
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @Auther: GMY
* @Date: 2022/09/16/9:54
* @Description: CSRF防護(hù)配置工具類
*/
@Component
public class CSRFFilterConfigUtils {
/**
* 跨站點(diǎn)請求路徑白名單,通過英文逗號分隔。在application.properties配置
*/
public static String csrfWhitePaths;
/**
* 跨站點(diǎn)請求域名白名單,通過英文逗號分隔。在application.properties配置
*/
public static String csrfWhiteDomains;
/**
* csrf攻擊防護(hù)開關(guān)配置
*/
public static Boolean openCsrfProtect;
/**
* @param
* @return java.lang.Boolean
* @author GMY
* @date 2022/9/16 10:13
* @description csrf攻擊防護(hù)開關(guān)配置,默認(rèn)為開啟
*/
public static Boolean getOpenCsrfProtect() {
return openCsrfProtect == null ? true : openCsrfProtect;
}
/**
* @param
* @return java.lang.String[]
* @author GMY
* @date 2022/9/16 10:07
* @description 獲取請求路徑白名單
*/
public static String[] getCsrfWhitePaths() {
if (StringUtils.isNotEmpty(csrfWhitePaths)) {
return csrfWhitePaths.split(",");
}
return null;
}
/**
* @param
* @return java.lang.String[]
* @author GMY
* @date 2022/9/16 10:09
* @description 獲取請求域名白名單
*/
public static String[] getCsrfWhiteDomains() {
if (StringUtils.isNotEmpty(csrfWhiteDomains)) {
return csrfWhiteDomains.split(",");
}
return null;
}
@Value("${csrf.white.paths}")
public static void setCsrfWhitePaths(String csrfWhitePaths) {
CSRFFilterConfigUtils.csrfWhitePaths = csrfWhitePaths;
}
@Value("${csrf.white.domains}")
public static void setCsrfWhiteDomains(String csrfWhiteDomains) {
CSRFFilterConfigUtils.csrfWhiteDomains = csrfWhiteDomains;
}
@Value("${open.csrf.protect}")
public void setOpenCsrfProtect(Boolean openCsrfProtect) {
CSRFFilterConfigUtils.openCsrfProtect = openCsrfProtect;
}
}
CsrfFilter 防護(hù)過濾類
import cn.hutool.json.JSONUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URL;
/**
* @Auther: GMY
* @Date: 2022/09/15/19:54
* @Description:
*/
@WebFilter(urlPatterns = "/*",filterName = "csrfFilter")
@Configuration
public class CsrfFilter implements Filter {
// 后臺日志打印
private Logger log = LoggerFactory.getLogger(CsrfFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
/**
* @param servletRequest
* @param servletResponse
* @param filterChain
* @return void
* @author GMY
* @date 2022/9/16 9:51
* @description 執(zhí)行CRSF過濾操作
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse res = (HttpServletResponse) servletResponse;
// 判斷CSRF防護(hù)是否開啟,如果沒開啟則直接略過過濾操作
if (!CSRFFilterConfigUtils.getOpenCsrfProtect()) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
String referer = req.getHeader("Referer");
if (!StringUtils.isBlank(referer)) {
// 獲取Referer參數(shù)中的地址和端口
String refererHostAndPort = getHostAndPort(req,referer);
// 獲取RequestURL參數(shù)中的地址和端口
String requestHostAndPort = getHostAndPort(req,null);
// 同域名和同端口,即同一個域的系統(tǒng),通過
if (requestHostAndPort.equalsIgnoreCase(refererHostAndPort)) {
filterChain.doFilter(servletRequest, servletResponse);
}else {
// 如果不同域名或端口,繼續(xù)判斷域名是否在白名單中,如果在白名單中則通過
if(isCsrfWhiteDomains(refererHostAndPort)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
// 獲取RequestURL參數(shù)中的路徑信息
String path = new URL(req.getRequestURL().toString()).getPath();
log.info("request請求路徑 path = " + path);
// 將路徑中的域名去除,只保留具體路徑
String actionPath = path.replaceAll(servletRequest.getServletContext().getContextPath(), "");
// 判斷路徑是否在訪問路徑白名單中,如果在白名單中,則通過,繼續(xù)后續(xù)執(zhí)行
if(isCsrfWhitePaths(actionPath)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
log.warn("csrf跨站點(diǎn)偽造請求已經(jīng)被攔截:");
log.warn("requestURL = " + req.getRequestURL().toString());
log.warn("referer = " + referer);
res.sendRedirect(req.getContextPath() + "/illegal");
return;
}
}else{
filterChain.doFilter(servletRequest, servletResponse);
}
}
}
/**
* @param request
* @param referer
* @return java.lang.String
* @author GMY
* @date 2022/9/16 9:34
* @description 獲取請求地址和端口
*/
protected String getHostAndPort(HttpServletRequest request, String referer) throws IOException {
URL url;
if (StringUtils.isNotEmpty(referer)) {
url = new URL(referer);
} else {
url = new URL(request.getRequestURL().toString());
}
String requestHostAndPort;
if(url.getPort() == -1) {
requestHostAndPort = url.getHost();
}else {
requestHostAndPort = url.getHost() + ":" + url.getPort();
}
return requestHostAndPort;
}
@Override
public void destroy() {
}
/**
* @param path
* @return boolean
* @author GMY
* @date 2022/9/16 9:52
* @description 判斷請求路徑是否在路徑白名單中
*/
private boolean isCsrfWhitePaths(String path) {
if(CSRFFilterConfigUtils.getCsrfWhitePaths() != null && CSRFFilterConfigUtils.getCsrfWhitePaths().length > 0) {
for (String csrfWhitePath : CSRFFilterConfigUtils.getCsrfWhitePaths()) {
if(!StringUtils.isBlank(csrfWhitePath)) {
if(csrfWhitePath.equals(path)) {
log.info("跨站點(diǎn)請求所有路徑白名單:csrfWhitePaths = " + JSONUtil.toJsonStr(CSRFFilterConfigUtils.getCsrfWhitePaths()));
log.info("符合跨站點(diǎn)請求路徑白名單:path = " + path);
return true;
}
}
}
}
return false;
}
/**
* @param refererHostAndPort
* @return boolean
* @author GMY
* @date 2022/9/16 9:52
* @description 判斷請求域名是否在域名白名單中
*/
private boolean isCsrfWhiteDomains(String refererHostAndPort) {
if(CSRFFilterConfigUtils.getCsrfWhiteDomains() != null && CSRFFilterConfigUtils.getCsrfWhiteDomains().length > 0) {
for (String csrfWhiteDomain : CSRFFilterConfigUtils.getCsrfWhiteDomains()) {
if(!StringUtils.isBlank(csrfWhiteDomain)) {
if(csrfWhiteDomain.equals(refererHostAndPort)) {
log.info("跨站點(diǎn)請求所有【域名】]白名單:csrfWhiteDomains = " + JSONUtil.toJsonStr(CSRFFilterConfigUtils.getCsrfWhiteDomains()));
log.info("符合跨站點(diǎn)請求【域名】白名單:refererHost = " + refererHostAndPort);
return true;
}
}
}
log.info("跨站點(diǎn)請求非法【域名】:refererHost = " + refererHostAndPort);
}
return false;
}
}
總結(jié)
以上代碼僅供學(xué)習(xí)交流使用,代碼中涉及到真實(shí)項(xiàng)目信息的內(nèi)容我都做了相應(yīng)修改
當(dāng)然,這些僅為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringMVC通過Ajax處理Json數(shù)據(jù)的步驟詳解
這篇文章主要介紹了SpringMVC通過Ajax處理Json數(shù)據(jù)的步驟詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
一文教你利用Stream?API批量Mock數(shù)據(jù)的方法
在日常開發(fā)的過程中我們經(jīng)常會遇到需要mock一些數(shù)據(jù)的場景,比如說?mock?一些接口的返回或者說?mock?一些測試消息用于隊(duì)列生產(chǎn)者發(fā)送消息。本文將教你如何通過?Stream?API?批量?Mock?數(shù)據(jù),需要的可以參考一下2022-09-09
SpringBoot實(shí)現(xiàn)簡單文件上傳功能
這篇文章主要為大家詳細(xì)介紹了SpringBoot實(shí)現(xiàn)簡單文件上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
java+Okhttp3調(diào)用接口的實(shí)例
這篇文章主要介紹了java+Okhttp3調(diào)用接口的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
Java異常區(qū)分和處理的一些經(jīng)驗(yàn)分享
這篇文章介紹了Java異常區(qū)分和處理的一些經(jīng)驗(yàn)分享,主要是異常選擇和使用中的一些誤區(qū)總結(jié)與歸納,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11

