• Springboot集成CSRF防攻击


    CSRF 就是跨域请求伪造,是一种常见的web攻击方式,解决思路也非常简单,主要是设置域名或路径白名单,对于未知的链接予以过滤,从而达到防护目的。

    总共两个类,一个CSRFFilterConfigUtils防护配置工具类,主要作用是配置防护开关、请求路径白名单以及请求域名白名单;一个是CsrfFilter防护过滤类,该类实质是一个拦截器,拦截所有用户请求,匹配路径和域名,符合条件的通过,不符合条件的拦截掉;

    以下为实际代码:

    CSRFFilterConfigUtils 防护配置工具类

    1. import org.apache.commons.lang.StringUtils;
    2. import org.springframework.beans.factory.annotation.Value;
    3. import org.springframework.stereotype.Component;
    4. /**
    5. * @Auther: GMY
    6. * @Date: 2022/09/16/9:54
    7. * @Description: CSRF防护配置工具类
    8. */
    9. @Component
    10. public class CSRFFilterConfigUtils {
    11. /**
    12. * 跨站点请求路径白名单,通过英文逗号分隔。在application.properties配置
    13. */
    14. public static String csrfWhitePaths;
    15. /**
    16. * 跨站点请求域名白名单,通过英文逗号分隔。在application.properties配置
    17. */
    18. public static String csrfWhiteDomains;
    19. /**
    20. * csrf攻击防护开关配置
    21. */
    22. public static Boolean openCsrfProtect;
    23. /**
    24. * @param
    25. * @return java.lang.Boolean
    26. * @author GMY
    27. * @date 2022/9/16 10:13
    28. * @description csrf攻击防护开关配置,默认为开启
    29. */
    30. public static Boolean getOpenCsrfProtect() {
    31. return openCsrfProtect == null ? true : openCsrfProtect;
    32. }
    33. /**
    34. * @param
    35. * @return java.lang.String[]
    36. * @author GMY
    37. * @date 2022/9/16 10:07
    38. * @description 获取请求路径白名单
    39. */
    40. public static String[] getCsrfWhitePaths() {
    41. if (StringUtils.isNotEmpty(csrfWhitePaths)) {
    42. return csrfWhitePaths.split(",");
    43. }
    44. return null;
    45. }
    46. /**
    47. * @param
    48. * @return java.lang.String[]
    49. * @author GMY
    50. * @date 2022/9/16 10:09
    51. * @description 获取请求域名白名单
    52. */
    53. public static String[] getCsrfWhiteDomains() {
    54. if (StringUtils.isNotEmpty(csrfWhiteDomains)) {
    55. return csrfWhiteDomains.split(",");
    56. }
    57. return null;
    58. }
    59. @Value("${csrf.white.paths}")
    60. public static void setCsrfWhitePaths(String csrfWhitePaths) {
    61. CSRFFilterConfigUtils.csrfWhitePaths = csrfWhitePaths;
    62. }
    63. @Value("${csrf.white.domains}")
    64. public static void setCsrfWhiteDomains(String csrfWhiteDomains) {
    65. CSRFFilterConfigUtils.csrfWhiteDomains = csrfWhiteDomains;
    66. }
    67. @Value("${open.csrf.protect}")
    68. public void setOpenCsrfProtect(Boolean openCsrfProtect) {
    69. CSRFFilterConfigUtils.openCsrfProtect = openCsrfProtect;
    70. }
    71. }

    CsrfFilter 防护过滤类

    1. import cn.hutool.json.JSONUtil;
    2. import org.apache.commons.lang.StringUtils;
    3. import org.slf4j.Logger;
    4. import org.slf4j.LoggerFactory;
    5. import org.springframework.context.annotation.Configuration;
    6. import javax.servlet.*;
    7. import javax.servlet.annotation.WebFilter;
    8. import javax.servlet.http.HttpServletRequest;
    9. import javax.servlet.http.HttpServletResponse;
    10. import java.io.IOException;
    11. import java.net.URL;
    12. /**
    13. * @Auther: GMY
    14. * @Date: 2022/09/15/19:54
    15. * @Description:
    16. */
    17. @WebFilter(urlPatterns = "/*",filterName = "csrfFilter")
    18. @Configuration
    19. public class CsrfFilter implements Filter {
    20. // 后台日志打印
    21. private Logger log = LoggerFactory.getLogger(CsrfFilter.class);
    22. @Override
    23. public void init(FilterConfig filterConfig) throws ServletException {
    24. }
    25. /**
    26. * @param servletRequest
    27. * @param servletResponse
    28. * @param filterChain
    29. * @return void
    30. * @author GMY
    31. * @date 2022/9/16 9:51
    32. * @description 执行CRSF过滤操作
    33. */
    34. @Override
    35. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    36. HttpServletRequest req = (HttpServletRequest) servletRequest;
    37. HttpServletResponse res = (HttpServletResponse) servletResponse;
    38. // 判断CSRF防护是否开启,如果没开启则直接略过过滤操作
    39. if (!CSRFFilterConfigUtils.getOpenCsrfProtect()) {
    40. filterChain.doFilter(servletRequest, servletResponse);
    41. } else {
    42. String referer = req.getHeader("Referer");
    43. if (!StringUtils.isBlank(referer)) {
    44. // 获取Referer参数中的地址和端口
    45. String refererHostAndPort = getHostAndPort(req,referer);
    46. // 获取RequestURL参数中的地址和端口
    47. String requestHostAndPort = getHostAndPort(req,null);
    48. // 同域名和同端口,即同一个域的系统,通过
    49. if (requestHostAndPort.equalsIgnoreCase(refererHostAndPort)) {
    50. filterChain.doFilter(servletRequest, servletResponse);
    51. }else {
    52. // 如果不同域名或端口,继续判断域名是否在白名单中,如果在白名单中则通过
    53. if(isCsrfWhiteDomains(refererHostAndPort)) {
    54. filterChain.doFilter(servletRequest, servletResponse);
    55. return;
    56. }
    57. // 获取RequestURL参数中的路径信息
    58. String path = new URL(req.getRequestURL().toString()).getPath();
    59. log.info("request请求路径 path = " + path);
    60. // 将路径中的域名去除,只保留具体路径
    61. String actionPath = path.replaceAll(servletRequest.getServletContext().getContextPath(), "");
    62. // 判断路径是否在访问路径白名单中,如果在白名单中,则通过,继续后续执行
    63. if(isCsrfWhitePaths(actionPath)) {
    64. filterChain.doFilter(servletRequest, servletResponse);
    65. return;
    66. }
    67. log.warn("csrf跨站点伪造请求已经被拦截:");
    68. log.warn("requestURL = " + req.getRequestURL().toString());
    69. log.warn("referer = " + referer);
    70. res.sendRedirect(req.getContextPath() + "/illegal");
    71. return;
    72. }
    73. }else{
    74. filterChain.doFilter(servletRequest, servletResponse);
    75. }
    76. }
    77. }
    78. /**
    79. * @param request
    80. * @param referer
    81. * @return java.lang.String
    82. * @author GMY
    83. * @date 2022/9/16 9:34
    84. * @description 获取请求地址和端口
    85. */
    86. protected String getHostAndPort(HttpServletRequest request, String referer) throws IOException {
    87. URL url;
    88. if (StringUtils.isNotEmpty(referer)) {
    89. url = new URL(referer);
    90. } else {
    91. url = new URL(request.getRequestURL().toString());
    92. }
    93. String requestHostAndPort;
    94. if(url.getPort() == -1) {
    95. requestHostAndPort = url.getHost();
    96. }else {
    97. requestHostAndPort = url.getHost() + ":" + url.getPort();
    98. }
    99. return requestHostAndPort;
    100. }
    101. @Override
    102. public void destroy() {
    103. }
    104. /**
    105. * @param path
    106. * @return boolean
    107. * @author GMY
    108. * @date 2022/9/16 9:52
    109. * @description 判断请求路径是否在路径白名单中
    110. */
    111. private boolean isCsrfWhitePaths(String path) {
    112. if(CSRFFilterConfigUtils.getCsrfWhitePaths() != null && CSRFFilterConfigUtils.getCsrfWhitePaths().length > 0) {
    113. for (String csrfWhitePath : CSRFFilterConfigUtils.getCsrfWhitePaths()) {
    114. if(!StringUtils.isBlank(csrfWhitePath)) {
    115. if(csrfWhitePath.equals(path)) {
    116. log.info("跨站点请求所有路径白名单:csrfWhitePaths = " + JSONUtil.toJsonStr(CSRFFilterConfigUtils.getCsrfWhitePaths()));
    117. log.info("符合跨站点请求路径白名单:path = " + path);
    118. return true;
    119. }
    120. }
    121. }
    122. }
    123. return false;
    124. }
    125. /**
    126. * @param refererHostAndPort
    127. * @return boolean
    128. * @author GMY
    129. * @date 2022/9/16 9:52
    130. * @description 判断请求域名是否在域名白名单中
    131. */
    132. private boolean isCsrfWhiteDomains(String refererHostAndPort) {
    133. if(CSRFFilterConfigUtils.getCsrfWhiteDomains() != null && CSRFFilterConfigUtils.getCsrfWhiteDomains().length > 0) {
    134. for (String csrfWhiteDomain : CSRFFilterConfigUtils.getCsrfWhiteDomains()) {
    135. if(!StringUtils.isBlank(csrfWhiteDomain)) {
    136. if(csrfWhiteDomain.equals(refererHostAndPort)) {
    137. log.info("跨站点请求所有【域名】]白名单:csrfWhiteDomains = " + JSONUtil.toJsonStr(CSRFFilterConfigUtils.getCsrfWhiteDomains()));
    138. log.info("符合跨站点请求【域名】白名单:refererHost = " + refererHostAndPort);
    139. return true;
    140. }
    141. }
    142. }
    143. log.info("跨站点请求非法【域名】:refererHost = " + refererHostAndPort);
    144. }
    145. return false;
    146. }
    147. }

    以上代码仅供学习交流使用,代码中涉及到真实项目信息的内容我都做了相应修改

  • 相关阅读:
    MIPI CSI-2笔记(19) -- 数据格式(用户自定义数据格式)
    2021年03月 Scratch(二级)真题解析#中国电子学会#全国青少年软件编程等级考试
    Linux 网络虚拟化 Macvlan(基于物理网络接口虚拟网络接口) 认知
    软件工程第一次作业参考答案
    数据结构与算法-第八章 插入排序
    使用 gpg 对Linux下的文件加密
    【infiniband】infiniband和RDMA
    【网页设计】HTML+CSS保护野生动物北极熊介绍网页设计专题
    华为机试HJ34
    HCIP第十五天笔记
  • 原文地址:https://blog.csdn.net/hao_kkkkk/article/details/127424169