Checkmarx标记含@Query的JPA非参数化查询方法为存储型XSS风险,如何解决?
问题分析与解决建议
这是Checkmarx的误报
你的JPA仓库方法不存在存储型XSS风险:
- 该自定义查询是完全静态的,没有引入任何用户可控的输入参数,不存在SQL注入或恶意内容注入的可能;
- 存储型XSS的风险核心是恶意内容写入数据库后,前端未转义直接渲染,而你的服务层已经通过
StringEscapeUtils::escapeHtml4对返回值做了转义,后续渲染的风险已被覆盖; - Checkmarx的扫描规则大概率只是简单匹配
@Query注解就触发告警,没有区分查询是否包含用户输入。
解决方式
1. 标记为误报
在Checkmarx平台找到对应告警,将其标记为误报,并补充说明:
该自定义查询无用户输入参数,仅做数据库字段拼接,返回值已在服务层完成HTML转义,不存在XSS风险。
2. 替换自定义@Query为Criteria API
如果想彻底绕过扫描规则,可用JPA Criteria API实现相同逻辑,避免使用@Query注解:
import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Root; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; public interface PersonRepository extends JpaRepository<PersonEntity, Long>, JpaSpecificationExecutor<PersonEntity> { default String[] findDistinctNames() { Specification<PersonEntity> spec = (Root<PersonEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> { // 拼接firstName和lastName var fullName = cb.concat(cb.concat(root.get("firstName"), cb.literal(" ")), root.get("lastName")); // 构建去重查询 query.select(fullName).distinct(true); return null; }; // 转换结果为字符串数组 return findAll(spec).stream() .map(Object::toString) .toArray(String[]::new); } }
3. 添加代码注释
在仓库方法上添加明确注释,帮助扫描工具或审核人员快速识别风险情况:
/** * 查询去重的人员全名,无用户输入参数,返回值已在服务层通过StringEscapeUtils::escapeHtml4做HTML转义 * 无存储型XSS风险 */ @Query("select distinct concat(p.firstName, ' ', p.lastName) as name from PersonEntity p") public String[] findDistinctNames();
4. 调整扫描规则(若有权限)
如果团队有权限修改Checkmarx的扫描规则,可以针对无参数的@Query方法调整检测逻辑,只对包含用户输入参数的自定义查询触发XSS告警。
内容的提问来源于stack exchange,提问作者Knight Steele
相关产品推荐
相关产品推荐

