Spring Boot应用XSS与SQL注入防护实战指南

Spring Boot应用XSS与SQL注入防护实战指南
1. 项目概述为什么Spring Boot应用必须重视XSS与SQL注入防护在Web应用开发领域尤其是使用Spring Boot这类高效框架时我们常常把精力集中在业务逻辑实现、性能优化和微服务架构上。然而一个功能再强大的应用如果其安全防线千疮百孔那么所有的努力都可能瞬间化为乌有。XSS跨站脚本攻击和SQL注入正是悬在Web应用头顶的两把“达摩克利斯之剑”。它们并非什么高深莫测的黑客技术反而是利用开发者疏忽、最常见也最危险的攻击手段。我见过太多项目在开发阶段对安全防护草草了事上线后一旦遭遇攻击轻则数据泄露、页面篡改重则服务器被控、核心业务瘫痪造成的损失和声誉影响难以估量。Spring Boot通过自动配置和约定大于配置的理念极大地简化了开发但它并不会自动为你筑起坚固的安全城墙。防护XSS和SQL注入是开发者必须主动承担的责任。简单来说XSS攻击是让恶意脚本在用户的浏览器中执行从而盗取用户会话、篡改页面内容或进行钓鱼欺诈。而SQL注入则是通过构造特殊的输入欺骗后端数据库执行非预期的SQL命令达到窃取、篡改甚至删除数据的目的。这两种攻击的根源都来自于对用户输入数据的不信任和未经验证的处理。本篇文章我将结合十多年的实战经验为你彻底拆解在Spring Boot项目中如何从编码习惯、框架特性、组件选型到部署配置构建一套立体、可落地的XSS与SQL注入防护体系。这不是一篇照本宣科的理论文章而是能让你“抄作业”的实战指南涵盖从原理到避坑的完整闭环。2. 安全威胁深度解析XSS与SQL注入的攻击原理与危害在动手搭建防护体系之前我们必须先成为“攻击者”深刻理解对手是如何工作的。只有知己知彼才能构建有效的防御。2.1 XSS攻击信任的滥用与脚本的“越狱”XSS的本质是“HTML注入”。攻击者发现Web应用在将用户输入的内容如评论、搜索关键词、URL参数输出到HTML页面时没有进行正确的转义或过滤从而使得输入内容中的HTML标签或JavaScript脚本被浏览器解析并执行。2.1.1 反射型XSS一次性的“钓鱼钩”这是最常见的一种。攻击者构造一个包含恶意脚本的URL并通过邮件、社交网站等渠道诱骗用户点击。当用户点击这个链接服务器接收到恶意参数后未加处理就直接将其拼接进响应页面并返回给用户的浏览器脚本随即执行。!-- 假设一个搜索功能URL为/search?keyword用户输入 -- !-- 正常搜索/search?keywordSpringBoot -- !-- 恶意攻击/search?keywordscriptalert(XSS)/script --如果后端代码简单地return “你搜索的关键词是” keyword; 那么script标签就会被浏览器执行。在实际攻击中这里的脚本可能是盗取用户Cookie并发送到攻击者服务器的代码。2.1.2 存储型XSS持久化的“毒药”这种攻击更为致命。恶意脚本被提交并存储到服务器的数据库或文件系统中如论坛帖子、用户昵称、商品评论。此后任何其他用户在浏览包含该数据的页面时都会触发恶意脚本的执行。它的危害范围广、持续时间长。例如攻击者在博客评论中插入一段窃取登录态的脚本之后所有查看该评论的用户都可能中招。2.1.3 DOM型XSS客户端的“内鬼”这种攻击不经过服务器。恶意数据在客户端被JavaScript动态操作DOM时不当使用导致脚本执行。例如// 从URL的hash中获取参数并直接写入DOM var input window.location.hash.substring(1); document.getElementById(“message”).innerHTML input;如果用户访问的URL是example.com#img src1 onerroralert(‘XSS’) 那么onerror事件中的脚本就会执行。实操心得很多开发者只防反射型和存储型忽略了DOM型XSS。尤其是在大量使用前端框架如Vue、React进行动态渲染时如果安全意识不足很容易引入此类漏洞。防护的关键在于任何来自不可信源URL参数、Cookie、第三方API返回的数据在用于操作DOM如innerHTML, document.write, eval前都必须进行净化或使用安全的API如textContent。2.2 SQL注入与数据库的“非法对话”SQL注入的原理更为直接攻击者在应用程序的输入字段如登录框、搜索框中插入精心构造的SQL代码片段。当应用程序将这些输入未经处理直接拼接到SQL查询语句中时攻击者的代码就成为原始查询的一部分被数据库执行。2.2.1 经典注入绕过身份验证假设登录查询的Java代码是String sql “SELECT * FROM users WHERE username ‘” username “‘ AND password ‘” password “‘”;攻击者在用户名输入admin’ —注意–是SQL注释符密码任意。拼接后的SQL变为SELECT * FROM users WHERE username ‘admin’ — ‘ AND password ‘xxx’—之后的内容被注释掉攻击者就能以管理员身份登录无需密码。2.2.2 联合查询注入窃取数据攻击者利用UNION SELECT语句将恶意查询结果附加到原始查询结果之后从而读取其他表的数据。例如输入1′ UNION SELECT username, password FROM users —。2.2.3 盲注没有回显的“猜谜”当页面不会直接显示数据库错误信息或查询结果时攻击者通过构造让SQL语句执行结果在页面响应如真/假、时间延迟上产生差异的Payload来一点点“盲猜”出数据库的结构和内容。例如输入1′ AND SLEEP(5) — 如果页面响应延迟了5秒说明注入成功。2.2.4 危害升级从数据泄露到服务器沦陷成功的SQL注入远不止窃取数据。攻击者可以利用数据库的特性执行更危险的操作读写服务器文件利用LOAD_FILE()和INTO OUTFILE函数。执行系统命令在某些数据库配置下如SQL Server的xp_cmdshell通过注入执行操作系统命令直接控制服务器。绕过WAF通过编码、注释符混淆、等价函数替换等方式绕过Web应用防火墙的检测规则。避坑指南永远不要抱有“我的SQL语句很简单不会被注入”的侥幸心理。任何将用户输入直接拼接成SQL字符串的地方都是潜在的风险点。即使是ORDER BY后面的字段名、LIMIT后面的分页参数如果来自用户输入且未经验证也可能成为注入点。3. Spring Boot立体防护体系构建从编码到部署理解了攻击原理我们就可以针对性地在Spring Boot应用的各个层面布防。防护不是单一技术而是一个从数据流入到流出的完整链条。3.1 第一道防线输入验证与数据绑定在数据刚进入应用时就进行严格的校验可以将大量畸形、恶意的请求拒之门外。3.1.1 使用JSR 380 (Bean Validation 2.0) 进行声明式验证Spring Boot天然整合了Hibernate Validator。不要只在保存数据时验证在Controller层接收参数时就应该开始。import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; Data public class UserDTO { NotBlank(message “用户名不能为空”) Size(min 3, max 20, message “用户名长度3-20位”) Pattern(regexp “^[a-zA-Z0-9_]$”, message “用户名只能包含字母、数字和下划线”) // 白名单规则拒绝特殊字符 private String username; // 对于搜索关键词可以限制长度和字符集 Size(max 100, message “搜索词过长”) Pattern(regexp “^[\\u4e00-\\u9fa5a-zA-Z0-9\\s\\-]$”, message “包含非法字符”) private String keyword; }在Controller中使用Valid注解触发验证PostMapping(“/register”) public ResponseEntity? register(RequestBody Valid UserDTO userDTO, BindingResult result) { if (result.hasErrors()) { // 返回详细的验证错误信息帮助前端提示但日志中要记录异常请求 return ResponseEntity.badRequest().body(result.getAllErrors()); } // 业务逻辑... }3.1.2 自定义验证器应对复杂场景对于更复杂的验证逻辑如业务规则校验、黑名单词过滤可以创建自定义验证器。Documented Constraint(validatedBy XssSafeValidator.class) Target({ElementType.FIELD, ElementType.PARAMETER}) Retention(RetentionPolicy.RUNTIME) public interface XssSafe { String message() default “内容包含潜在的不安全脚本”; Class?[] groups() default {}; Class? extends Payload[] payload() default {}; } public class XssSafeValidator implements ConstraintValidatorXssSafe, String { // 这里可以引入一个简单的正则或关键词库进行初步过滤 private static final Pattern[] XSS_PATTERNS { Pattern.compile(“script”, Pattern.CASE_INSENSITIVE), Pattern.compile(“javascript:”, Pattern.CASE_INSENSITIVE), Pattern.compile(“on\\w”, Pattern.CASE_INSENSITIVE) // onclick, onerror等 }; Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value null) return true; for (Pattern pattern : XSS_PATTERNS) { if (pattern.matcher(value).find()) { // 记录安全日志 log.warn(“检测到潜在的XSS攻击输入: {}”, value); return false; } } return true; } }注意事项输入验证应采用“白名单”原则只允许已知好的字符而非“黑名单”试图阻止已知坏的字符。黑名单永远无法穷尽所有攻击变种。上述自定义验证器仅作为补充和日志记录手段绝不能作为唯一的XSS防护措施。3.2 核心防御输出编码与参数化查询这是防护XSS和SQL注入最核心、最有效的手段。3.2.1 防御XSS在正确的上下文中进行输出编码XSS防护的黄金法则是任何不可信的数据在输出到不同上下文时必须使用对应的编码函数。HTML上下文将,,,”,’等字符转换为HTML实体如-lt;。HTML属性上下文除了HTML实体转义还要注意属性值用引号包裹。JavaScript上下文需要将数据放入JS字符串时需进行JS转义如\”,\\,\n。URL上下文进行URL编码百分号编码。在Spring Boot中我们有多种实现方式方案一依赖模板引擎的自动转义推荐如果你使用ThymeleafSpring Boot默认推荐它默认对所有表达式输出进行HTML转义。这是最简单有效的防护。!-- 在Thymeleaf模板中以下方式是安全的 -- p th:text”${userInput}”/p !-- 自动转义 -- div th:utext”${trustedHtml}”/div !-- utext 表示不转义慎用 --确保你的Thymeleaf配置没有关闭转义默认就是开启的。对于Freemarker和Velocity也需要确认其自动转义功能已启用。方案二在服务端手动编码后输出如果前后端分离后端提供JSON API那么需要在将数据放入响应体之前或者在JSON序列化过程中进行编码。可以使用org.springframework.web.util.HtmlUtils。import org.springframework.web.util.HtmlUtils; public class ApiResponse { private String content; // Getter中编码 public String getContent() { return HtmlUtils.htmlEscape(this.content); // 进行HTML转义 } // 或者在设置内容时就编码 public void setSafeContent(String rawContent) { this.content HtmlUtils.htmlEscape(rawContent); } }对于更复杂的场景可以考虑使用OWASP Java Encoder项目提供的Encoder类它提供了针对不同上下文的编码方法。import org.owasp.encoder.Encode; // ... String safeForHtml Encode.forHtmlContent(userInput); String safeForJs Encode.forJavaScript(userInput); String safeForAttr Encode.forHtmlAttribute(userInput);方案三前端框架的防护现代前端框架如React、Vue、Angular在默认情况下都会对渲染到模板中的数据自动进行转义这提供了另一层防护。但切记使用v-html(Vue)或dangerouslySetInnerHTML(React)等特性时等同于告诉框架“我信任这段HTML”此时必须确保内容来源绝对安全或已自行净化。3.2.2 防御SQL注入永远使用参数化查询PreparedStatement这是根治SQL注入的“银弹”。它的原理是将SQL语句的结构命令、表名、列名与数据用户输入的值分开发送数据库。数据库会先编译SQL结构然后将输入的值仅仅当作“数据”来处理而不是可执行的代码。在Spring Boot中我们有多种优雅的方式方案一Spring Data JPA / Hibernate最省心使用CrudRepository或JpaRepository其方法查询和Query注解默认使用参数绑定天然防注入。public interface UserRepository extends JpaRepositoryUser, Long { // 方法名查询 - 安全 User findByUsername(String username); // Query 注解 - 使用命名参数或位置参数安全 Query(“SELECT u FROM User u WHERE u.username :uname AND u.email :email”) User findUser(Param(“uname”) String username, Param(“email”) String email); // 原生SQL查询 - 也必须使用参数绑定 Query(value “SELECT * FROM users WHERE username ?1 AND status ?2”, nativeQuery true) User findNativeUser(String username, int status); }方案二JdbcTemplate灵活且安全Spring的JdbcTemplate强制要求使用参数占位符(?)和参数列表有效防止注入。Autowired private JdbcTemplate jdbcTemplate; public User findUser(String username, String password) { // 正确做法使用参数占位符 String sql “SELECT * FROM users WHERE username ? AND password ?”; // jdbcTemplate会自动处理参数转义 return jdbcTemplate.queryForObject(sql, new Object[]{username, password}, new UserRowMapper()); } // 绝对禁止的写法拼接字符串 // String sql “SELECT * FROM users WHERE username ‘” username “‘ AND password ‘” password “‘”; // 高危方案三MyBatis注意${}和#{}的区别MyBatis中#{}是参数占位符会进行预编译是安全的。而${}是字符串替换直接将值拼接到SQL语句中存在SQL注入风险应仅用于动态指定列名、表名等不可信数据来源的场景且使用时必须非常谨慎最好结合白名单验证。!-- 安全使用 #{} -- select id”selectUser” resultType”User” SELECT * FROM user WHERE username #{username} /select !-- 危险使用 ${} 拼接用户输入 -- select id”selectOrder” resultType”Order” SELECT * FROM orders ORDER BY ${orderBy} !-- 如果orderBy来自用户输入则危险 -- /select !-- 相对安全使用 ${} 但参数是内部枚举或经过白名单校验 -- select id”selectWithDynamicTable” resultType”Map” SELECT * FROM ${tableName} where if test”type ! null” AND type #{type} /if /where /select // Java代码中调用该方法前必须对tableName进行白名单校验 public ListMap selectFromTable(String tableName) { ListString allowedTables Arrays.asList(“user”, “order”, “product”); if (!allowedTables.contains(tableName.toLowerCase())) { throw new IllegalArgumentException(“Invalid table name”); } return sqlSession.selectList(“selectWithDynamicTable”, tableName); }核心经验在你的项目中全局搜索${MyBatis和字符串拼接的SQL语句这是排查SQL注入漏洞最直接有效的方法。将${}的使用限制在最小范围并确保其参数是内部可控或经过严格白名单校验的。3.3 增强防护安全HTTP头、WAF与依赖管理除了应用层代码基础设施和配置也能提供强大的纵深防御。3.3.1 利用安全HTTP头Spring Security可以方便地配置安全相关的HTTP响应头它们像给浏览器下达的“安全指令”。Content-Security-Policy (CSP)这是防御XSS的终极利器。它告诉浏览器只允许加载和执行来自指定来源的脚本、样式、图片等资源。即使攻击者成功注入了脚本如果来源不在白名单内浏览器也不会执行。Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http // … 其他配置 … .headers() .contentSecurityPolicy(“script-src ‘self’ https://trusted.cdn.com; object-src ‘none’;”); // 只允许同源和指定CDN的脚本禁止插件 } }X-Content-Type-Options: nosniff阻止浏览器进行MIME类型嗅探降低某些基于文件上传的XSS风险。X-Frame-Options: DENY防止页面被嵌入到iframe中用于对抗点击劫持。Strict-Transport-Security (HSTS)强制浏览器使用HTTPS连接。3.3.2 考虑Web应用防火墙WAF对于大型或对安全要求极高的应用可以在Spring Boot应用前部署一层WAF如ModSecurity或云服务商提供的WAF。WAF基于规则库可以识别和拦截常见的攻击模式如SQL注入、XSS的特征字符串为应用提供一道额外的屏障。但要注意WAF不能替代安全的编码实践它只是缓解措施且可能存在误拦和绕过风险。3.3.3 管理项目依赖及时修复漏洞使用spring-boot-starter-parent管理版本并定期运行mvn dependency:tree或gradle dependencies检查依赖使用OWASP Dependency-Check或GitHub Dependabot等工具扫描已知漏洞CVE。一个脆弱的第三方库如旧版本的MyBatis、Fastjson、Jackson可能成为整个系统的突破口。4. 实战演练构建一个具备基础防护的Spring Boot API让我们通过一个简单的用户评论API将上述防护措施整合起来。4.1 项目结构与依赖创建一个标准的Spring Boot项目主要依赖dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdcom.h2database/groupId artifactIdh2/artifactId scoperuntime/scope /dependency !-- OWASP Encoder (可选用于更灵活的编码) -- dependency groupIdorg.owasp.encoder/groupId artifactIdencoder/artifactId version1.2.3/version /dependency /dependencies4.2 定义实体与DTO包含输入验证// Comment.java (Entity) Entity Data public class Comment { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String author; // 存储时已是安全内容 private String content; // 存储时已是安全内容 private LocalDateTime createTime; } // CommentDTO.java Data public class CommentDTO { NotBlank(message “作者不能为空”) Size(max 50, message “作者名过长”) Pattern(regexp “^[\\u4e00-\\u9fa5a-zA-Z0-9\\-\\s]$”, message “作者名包含非法字符”) private String author; NotBlank(message “评论内容不能为空”) Size(max 1000, message “评论内容过长”) // 可以加上自定义的XSS初步过滤注解 XssSafe private String content; }4.3 实现Service层处理业务逻辑与编码Service Slf4j public class CommentService { Autowired private CommentRepository commentRepository; Autowired private HtmlUtils htmlUtils; // 假设我们注入了一个工具类 public Comment createComment(CommentDTO commentDTO) { // 1. DTO的验证已在Controller层通过Valid完成 // 2. 对即将存储到数据库的内容进行HTML转义净化 String safeAuthor HtmlUtils.htmlEscape(commentDTO.getAuthor().trim()); String safeContent HtmlUtils.htmlEscape(commentDTO.getContent().trim()); // 3. 也可以使用OWASP Encoder进行更精确的编码如果上下文复杂 // String safeContentForHtml Encode.forHtmlContent(commentDTO.getContent()); // 记录原始输入用于审计生产环境可存日志系统 log.debug(“Received comment - Raw: {}, Sanitized: {}”, commentDTO.getContent(), safeContent); Comment comment new Comment(); comment.setAuthor(safeAuthor); comment.setContent(safeContent); comment.setCreateTime(LocalDateTime.now()); // 4. 使用Spring Data JPA保存天然防SQL注入 return commentRepository.save(comment); } public ListComment getAllComments() { // 直接返回因为存储时已转义。如果前端是JSON API这里返回的对象会被序列化为JSON。 // JSON序列化器默认会对字符串进行适当的转义如将”转义为\”但这不同于HTML转义。 // 如果前端直接使用此JSON数据插入HTML仍需前端进行编码。 // 更佳实践API返回数据由前端根据渲染上下文自行编码。 return commentRepository.findAll(); } }4.4 实现Controller层RestController RequestMapping(“/api/comments”) Validated public class CommentController { Autowired private CommentService commentService; PostMapping public ResponseEntityComment addComment(RequestBody Valid CommentDTO commentDTO, BindingResult result) { // Valid 会自动触发验证错误信息在result中 if (result.hasErrors()) { // 生产环境应返回更友好的错误信息而非全部细节防止信息泄露 throw new ValidationException(“输入参数验证失败”); } Comment savedComment commentService.createComment(commentDTO); return ResponseEntity.ok(savedComment); } GetMapping public ResponseEntityListComment getComments() { return ResponseEntity.ok(commentService.getAllComments()); } }4.5 配置全局异常处理与安全响应头ControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntityMapString, String handleValidationExceptions(MethodArgumentNotValidException ex) { MapString, String errors new HashMap(); ex.getBindingResult().getFieldErrors().forEach(error - errors.put(error.getField(), error.getDefaultMessage())); // 返回400和错误信息注意不要暴露堆栈等敏感信息 return ResponseEntity.badRequest().body(errors); } } Configuration public class SecurityHeaderConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { // 可以添加拦截器进行更全局的请求/响应处理 } Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.defaultContentType(MediaType.APPLICATION_JSON); } } // 通过application.properties配置安全头更简单 // server.servlet.session.cookie.http-onlytrue # 保护Cookie // server.servlet.session.cookie.securetrue # 仅HTTPS传输Cookie (生产环境)5. 进阶防护与常见问题排查即使遵循了最佳实践在复杂的业务场景和持续迭代中安全问题仍可能悄然出现。5.1 富文本内容HTML的处理难题用户评论、文章发布等场景需要保留一些安全的HTML格式如加粗、链接、图片。这时简单的转义会破坏格式。解决方案是使用专业的HTML净化库只允许安全的标签和属性通过。推荐使用OWASP Java HTML Sanitizerimport org.owasp.html.HtmlPolicyBuilder; import org.owasp.html.PolicyFactory; public class HtmlSanitizerUtil { private static final PolicyFactory POLICY new HtmlPolicyBuilder() .allowElements(“p”, “br”, “b”, “i”, “u”, “strong”, “em”, “a”, “img”) .allowUrlProtocols(“https”, “http”) // 只允许网络URL .allowAttributes(“href”).onElements(“a”).requireRelNoFollow() // 链接增加nofollow .allowAttributes(“src”).onElements(“img”) .allowAttributes(“alt”).onElements(“img”) .allowStandardUrlProtocols() .toFactory(); public static String sanitize(String dirtyHtml) { if (dirtyHtml null) return null; return POLICY.sanitize(dirtyHtml); // 返回安全的HTML片段 } }在Service层对需要富文本的字段用sanitize()方法替换简单的htmlEscape()。踩坑记录曾经遇到一个案例项目使用了过时的富文本编辑器且未做净化攻击者通过构造img src1 onerrorstealCookie()这样的标签属性执行了XSS。引入Sanitizer后onerror属性会被直接剥离。务必根据业务需求严格定义白名单标签和属性。5.2 MyBatis中${}的动态排序与分页安全这是SQL注入的高危区。解决方案是白名单校验。Service public class ProductService { private static final MapString, String COLUMN_WHITELIST new HashMap(); static { COLUMN_WHITELIST.put(“price”, “price”); COLUMN_WHITELIST.put(“createTime”, “create_time”); COLUMN_WHITELIST.put(“name”, “name”); } public ListProduct getProducts(String sortBy, String order) { // 1. 校验排序字段 String columnName COLUMN_WHITELIST.get(sortBy); if (columnName null) { columnName “create_time”; // 默认值 } // 2. 校验排序方向 if (!“asc”.equalsIgnoreCase(order) !“desc”.equalsIgnoreCase(order)) { order “desc”; } // 3. 使用MyBatis的${}但此时参数是经过白名单校验的安全值 return productMapper.selectWithOrder(columnName, order); } } // MyBatis Mapper // select id”selectWithOrder” resultType”Product” // SELECT * FROM product ORDER BY ${sortColumn} ${sortOrder} // /select5.3 常见的防护误区与排查清单误区1只在保存时转义读取时直接使用。正解存储“干净”的数据已转义或净化。这样无论数据被如何使用、被哪个系统访问都是安全的。这被称为“存储时净化”。误区2依赖前端进行XSS防护。正解安全防护必须前后端同时进行纵深防御但后端必须作为最后且最可靠的防线。攻击者可以绕过浏览器直接调用API。误区3使用正则表达式完全过滤XSS。正解XSS的变种极其繁多正则难以穷尽且维护困难。对于普通文本输出使用转义库对于富文本使用净化库。误区4认为使用了ORM框架就绝对安全。正解JPA的Query如果使用原生SQL并拼接字符串同样危险。MyBatis的${}是危险的。必须坚持使用参数化查询。安全自查清单输入验证所有API接口是否都对入参使用了Valid进行校验校验规则是否足够严格白名单输出编码所有从数据库取出或用户输入最终要输出到HTML页面的数据是否都经过了正确的上下文编码模板引擎自动转义是否开启SQL查询代码中是否存在字符串拼接的SQLMyBatis XML中是否使用了${}且参数来自用户输入富文本处理如果需要富文本是否使用了专业的HTML净化库白名单是否最小化安全头是否配置了CSP、X-Frame-Options等安全HTTP头依赖安全是否定期扫描并升级存在已知漏洞的第三方依赖错误处理应用是否返回了过于详细的数据库错误信息给前端这会给SQL注入攻击提供信息日志记录是否记录了所有可疑的输入如触发XSS过滤规则的请求用于事后审计5.4 渗透测试与自动化扫描除了自查引入外部视角至关重要。使用ZAP或Burp Suite进行主动扫描对应用进行自动化漏洞扫描可以发现常见的XSS和SQL注入点。进行代码审计使用SonarQube、Fortify等静态代码分析工具可以识别出潜在的不安全代码模式。定期进行人工渗透测试聘请专业的安全人员或让开发团队内部进行交叉测试模拟真实攻击。防护XSS和SQL注入是一场持久战它贯穿于设计、编码、测试、部署和运维的全生命周期。在Spring Boot中我们拥有强大的工具和生态来简化这项工作但最关键的始终是开发人员心中那根紧绷的“安全弦”。记住一个原则永远不要信任用户输入的任何数据。将其作为所有安全实践的出发点你的应用就成功了一大半。