当前位置: 首页 > news >正文

防 XSS和CSRF 过滤器(Filter)

会话管理存在问题:

1.服务集群部署或者是分布式服务如何实现会话共享

2.会话的不同存储地方的安全性问题

答:

会话共享 可以使用后端集中管理(redis)或者客户端管理 (jwt);

存储安全性 这个还真的没有太好的方式,需要配合各种防护策略进行防,特别是基于cookie的前端管理,就算策略很难被攻破,但是有存在用户禁用cookie无法完成会话正常传递问题;所以cookie这种方式就不考虑,基于localStorage虽然可以避免csrf的直接攻击,但是又存在被XSS攻击的可能,所以还要对入参进行检验,防脚本攻击。

package com.example.security.filter;import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;import java.io.IOException;@Component
public class XssFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;XssHttpServletRequestWrapper wrappedRequest = new XssHttpServletRequestWrapper(httpRequest);chain.doFilter(wrappedRequest, response);}
}

 2. 创建 Request 包装类用于转义参数

package com.example.security.filter;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.util.HashMap;
import java.util.Map;public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {public XssHttpServletRequestWrapper(HttpServletRequest request) {super(request);}@Overridepublic String[] getParameterValues(String parameter) {String[] values = super.getParameterValues(parameter);if (values == null) return null;int count = values.length;String[] encodedValues = new String[count];for (int i = 0; i < count; i++) {encodedValues[i] = cleanXSS(values[i]);}return encodedValues;}@Overridepublic String getParameter(String parameter) {String value = super.getParameter(parameter);return value == null ? null : cleanXSS(value);}@Overridepublic Map<String, String[]> getParameterMap() {Map<String, String[]> map = new HashMap<>(super.getParameterMap());Map<String, String[]> cleanedMap = new HashMap<>();for (Map.Entry<String, String[]> entry : map.entrySet()) {String[] values = entry.getValue();if (values != null) {String[] cleanedValues = new String[values.length];for (int i = 0; i < values.length; i++) {cleanedValues[i] = cleanXSS(values[i]);}cleanedMap.put(entry.getKey(), cleanedValues);}}return cleanedMap;}private String cleanXSS(String value) {// 简单的 XSS 转义,也可以使用 OWASP 的 AntiSamy 或 Jsoupreturn value.replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\$", "&#40;").replaceAll("\$", "&#41;").replaceAll("'", "&#39;").replaceAll("\"", "&quot;");}
}

3.注册 Filter

package com.example.config;import com.example.security.filter.XssFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class WebConfig {@Beanpublic FilterRegistrationBean<XssFilter> xssFilterRegistration() {FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();// 创建过滤器实例registration.setFilter(new XssFilter());// 设置过滤路径,/* 表示拦截所有请求registration.addUrlPatterns("/*");// 设置过滤器名称registration.setName("XssFilter");// 设置加载顺序,数字越小优先级越高registration.setOrder(1);return registration;}
}
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;public class CsrfFilter implements Filter {private String csrfToken;private Set<String> excludedPaths = new HashSet<>();@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 生成 TokencsrfToken = UUID.randomUUID().toString();// 从配置中读取要排除的路径String excludedUrls = filterConfig.getInitParameter("excludedUrls");if (excludedUrls != null && !excludedUrls.isEmpty()) {excludedPaths.addAll(Arrays.asList(excludedUrls.split(",")));}}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;HttpServletResponse httpResponse = (HttpServletResponse) response;String uri = httpRequest.getRequestURI();// 如果是免校验路径,直接放行if (isExcludedPath(uri)) {chain.doFilter(request, response);return;}// 只对敏感方法(POST/PUT/DELETE)做 CSRF 校验if (isSensitiveMethod(httpRequest.getMethod())) {String clientToken = httpRequest.getHeader("X-CSRF-TOKEN");if (clientToken == null || !csrfToken.equals(clientToken)) {httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid CSRF Token");return;}}// 返回当前 token 给前端(可选)httpResponse.setHeader("X-CSRF-TOKEN", csrfToken);chain.doFilter(request, response);}private boolean isExcludedPath(String uri) {return excludedPaths.stream().anyMatch(uri::startsWith);}private boolean isSensitiveMethod(String method) {return "POST".equalsIgnoreCase(method) ||"PUT".equalsIgnoreCase(method) ||"DELETE".equalsIgnoreCase(method);}}

 注册 CsrfFilter

@Bean
public FilterRegistrationBean<CsrfFilter> csrfFilterRegistration() {FilterRegistrationBean<CsrfFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new CsrfFilter());registration.addUrlPatterns("/*");registration.addInitParameter("excludedUrls", "/login,/register");registration.setName("CsrfFilter");registration.setOrder(1);return registration;
}
在登录成功后生成并设置 Token
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request, HttpSession session) {String csrfToken = UUID.randomUUID().toString();session.setAttribute("X-CSRF-TOKEN", csrfToken);return ResponseEntity.ok().header("X-CSRF-TOKEN", csrfToken).build();
}

3. 前端处理
为了让 CSRF Token 被正确发送,前端需要从响应头中提取 X-CSRF-TOKEN 并在后续的 POST 请求中将其作为头部信息发送回去。

http://www.dtcms.com/a/264122.html

相关文章:

  • go语言安装达梦数据完整教程
  • JVM 中的垃圾回收算法及垃圾回收器详解
  • 【仿muduo库实现并发服务器】Connection模块
  • CentOS 8中 更新或下载时报错:为仓库 ‘appstream‘ 下载元数据失败 : Cannot prepare internal
  • 02.SpringBoot常用Utils工具类详解
  • 从马赛克到色彩错乱:一次前景图像处理异常的全流程踩坑记录
  • Python实例题:基于 Python 的简单爬虫与数据可视化
  • 【IP 潮玩行业深度研究与学习】
  • 【仿muduo库实现并发服务器】eventloop模块
  • 香橙派3B学习笔记14:deb 打包程序_解包前后脚本运行
  • 折线图多数据处理
  • redux基本概念介绍 与 更新方式
  • 【网工|知识升华版|理论】ARQ机制|CSMA/CD协议
  • NetSuite 中如何在已关账期间内Unapply Customer Payment?
  • 数据结构day6——内核链表
  • 手机屏色斑缺陷修复及相关液晶线路激光修复原理
  • 一文讲清楚React合成事件机制和this的绑定问题
  • Pycharm命令行能运行,但绿色三角报错?
  • 51单片机制作万年历
  • java web2(黑马)
  • [Python] -基础篇7-新手常见Python语法错误及解决方案
  • 论文阅读:BLIPv1 2022.2
  • Java 大视界 -- Java 大数据在智能交通共享单车智能调度与停放管理中的应用(329)
  • 基于Pandas和FineBI的昆明职位数据分析与可视化实现(四)- 职位数据可视化(FineBI)
  • 解决leetcode第3597题分割字符串
  • 【一起来学AI大模型】模型上下文协议(MCP)详解:背景、架构与应用
  • 从0开始学习R语言--Day35--核密度动态估计
  • Node.js-path模块
  • 12【进程间通信——管道】
  • 记本好书:矩阵力量:线性代数全彩图解+微课+Python编程