3月20日紧急修复|Spring AI双漏洞CVE-2026-22730/22729实战防护方案
文章目录
无意间发现了一个CSDN大神的人工智能教程,忍不住分享一下给大家。很通俗易懂,重点是还非常风趣幽默,像看小说一样。床送门放这了👉 http://blog.csdn.net/jiangjunshow
前言
兄弟们,Spring AI这波"背刺"来得有点猛。
说真的,我昨天还在跟同事吹牛逼,说咱们这套基于Spring AI的RAG系统稳如老狗,结果晚上刷安全通告直接傻眼——Spring官方在3月17号(没错,就是三天前)连发两条高危漏洞公告,CVSS评分分别飙到8.8和8.6,属于那种"看一眼就睡不着觉"的级别。
这俩漏洞编号记好了:CVE-2026-22730 和 CVE-2026-22729。一个是SQL注入,一个是JSONPath注入,组合拳打得虎虎生风。更蛋疼的是,只要你用的是Spring AI 1.0.x或者1.1.x版本,基本都在射程范围内。说白了,只要你用了向量数据库做过滤查询,就相当于给黑客留了扇后门,人家拿着万能钥匙随时能进来翻你家冰箱。
我花了一晚上时间复现这两个漏洞,今天赶紧把热乎的防护方案写出来。咱不整那些虚头八脑的安全理论,直接上手怎么检查、怎么修、怎么防,争取让你喝杯咖啡的功夫把坑填上。
这俩漏洞到底是个啥"鬼东西"
CVE-2026-22730:SQL注入的"借尸还魂"
先说这个评分8.8的狠角色。问题出在MariaDBFilterExpressionConverter这个类上,这玩意是干嘛的呢?简单理解,就是你用Spring AI查MariaDB向量数据库时,它负责把你的过滤条件翻译成SQL语句。
正常情况下,这个过程应该像银行柜台一样,柜员(代码)严格按照规定流程办事,客户(用户输入)说啥都不管用。但这次的bug在于,柜员太老实了,客户说"我要查ID等于1的记录",柜员就原封不动地写进SQL。万一客户说的是1 OR 1=1这种黑话,柜员也照办,那不就全漏了?
具体来说,当你用FilterExpressionBuilder拼查询条件时,如果直接把用户输入的字符串塞进去,比如:
FilterExpressionBuilder b = new FilterExpressionBuilder();
b.eq("userId", request.getUserId()); // 这里userId如果是恶意构造的...
如果userId被填成了1" OR "1"="1这种 payload,因为没有做充分的转义处理,最后生成的SQL就变成了:
SELECT * FROM vector_store WHERE metadata->>"$.userId" == "1" OR "1"="1
好家伙,这后面的OR "1"="1"一加上去,直接变成查询所有记录了。这就是典型的SQL注入,只不过套了个向量数据库的马甲。
CVE-2026-22729:JSONPath的"暗度陈仓"
第二个漏洞评分8.6,原理类似,但玩的是JSONPath注入。这次中招的是AbstractFilterExpressionConverter,也就是所有向量存储过滤器的"祖宗类"。
JSONPath这玩意儿,可以理解为在JSON文档里找东西的"导航系统"。正常情况下,你应该只能查自己有权限看的数据,比如.tenantId == "user123"。但如果系统没处理好,攻击者可以注入额外的JSONPath逻辑,比如改成.tenantId == "user123" || $.role == "admin",甚至直接把整个查询逻辑改写。
最骚的是,这漏洞能直接绕过多租户隔离。想象一下,你开发了个SaaS系统,每个客户只能看自己的文档,结果因为这么个过滤条件没处理好,客户A随便构造个请求就能看到客户B的私密数据,这不就全完了?
快看看你是不是"中枪"了
别光听我在这吓唬人,先看看你的项目有没有踩雷。检查方法很简单,打开你的pom.xml或build.gradle:
Maven用户看这里:
org.springframework.ai
spring-ai-mariadb-store
1.0.3
Gradle用户看这里:
implementation 'org.springframework.ai:spring-ai-mariadb-store:1.1.2' // 同样是高危版本
受影响的版本范围
- Spring AI 1.0.0 - 1.0.3(1.0线全灭)
- Spring AI 1.1.0 - 1.1.2(1.1线也全灭)
如果你用的是其他向量存储(比如Redis、MongoDB、PgVector),也别高兴太早。虽然CVE-2026-22730只针对MariaDB,但CVE-2026-22729影响的是AbstractFilterExpressionConverter这个基类,只要你用了过滤表达式功能,理论上都可能有风险。
紧急修复:三步走战略
第一步:升级(最靠谱的方案)
Spring官方已经发布了修复版本:
- 1.0.x用户升级到 1.0.4
- 1.1.x用户升级到 1.1.3
Maven直接改版本号:
1.1.3
Gradle同理:
ext {
set('springAiVersion', "1.1.3")
}
升级完之后,记得跑一遍你的集成测试,特别是那些涉及向量搜索、元数据过滤的用例。Spring AI的API在1.0到1.1之间有些breaking changes,虽然1.0.4是patch版本,但小心驶得万年船。
第二步:临时缓解措施(万一升不了级)
我知道有些 legacy 项目,牵一发而动全身,升级版本可能要排期到下周甚至下个月。在这种"青黄不接"的时候,你得先加个"止血带"——输入验证。
既然漏洞是因为用户输入直接拼进查询导致的,那咱们就手动过滤一下危险字符。可以搞个简单的工具类:
@Component
public class FilterInputSanitizer {
// 这些字符在JSONPath和SQL里都是危险分子
private static final Pattern DANGEROUS_CHARS = Pattern.compile("[\"'\\\\&|!]");
public String sanitize(String input) {
if (input == null) {
return null;
}
// 简单粗暴但有效:把危险字符全干掉或转义
return DANGEROUS_CHARS.matcher(input).replaceAll("");
}
}
然后在你的Repository层加上这层防护:
@Service
public class DocumentService {
@Autowired
private FilterInputSanitizer sanitizer;
@Autowired
private VectorStore vectorStore;
public List search(String userId, String query) {
// 先洗一遍输入
String safeUserId = sanitizer.sanitize(userId);
SearchRequest request = SearchRequest.builder()
.query(query)
.filterExpression("userId == '" + safeUserId + "'") // 注意:即使做了sanitize,这种拼接方式依然不推荐,只是权宜之计
.build();
return vectorStore.similaritySearch(request);
}
}
注意:这只是临时方案,千万别觉得这样就高枕无忧了。真正的安全还得靠升级。
第三步:改代码逻辑(治本之策)
其实就算没这漏洞,直接拼接查询字符串本来就是个坏习惯。Spring AI提供了参数化查询的方式,或者说你应该把过滤条件拆得更细,别给用户直接操作查询字符串的机会。
推荐的做法是用Filter.Expression的API,而不是直接传字符串:
// 不推荐:直接把用户输入拼进表达式
String expr = "author == '" + userInput + "'";
// 推荐:用类型安全的方式构建
FilterExpressionBuilder builder = new FilterExpressionBuilder();
Expression expr = builder.eq("author", userInput).build(); // 升级后的版本会正确处理转义
实战检测:写个脚本扫一遍
光修代码不够,你还得确认生产环境到底有没有被捅过。虽然这两个漏洞刚披露三天,被大规模利用的可能性还不高,但检查一下日志总是好的。
可以写个简单的Python脚本(或者Java,随你喜好)扫一下你的访问日志,看看有没有可疑的查询参数:
import re
# 可疑的payload特征
suspicious_patterns = [
r'"\sOR\s"1"\s*=\s"1', # SQL注入经典payload
r'||.==', # JSONPath注入尝试
r'&&\s*[^\s]+==', # 逻辑运算符注入
r'"[^"]".==.*"' # 引号逃逸尝试
]
def check_logs(log_file):
with open(log_file, 'r') as f:
for line_num, line in enumerate(f, 1):
for pattern in suspicious_patterns:
if re.search(pattern, line, re.IGNORECASE):
print(f"[ALERT] 第{line_num}行发现可疑请求: {line.strip()}")
break
if __name__ == "__main__":
check_logs("/var/log/app/access.log")
当然,如果你用了ELK或者Grafana Loki这种日志系统,直接在里面搜关键词更快。重点搜||、&&、==这些在过滤表达式里不该出现的组合。
深层次思考:为什么我们会"翻车"
这次漏洞给我最大的感触是——别把用户当好人。Spring AI作为一个相对年轻的框架(1.0正式版其实也就这一年多的事),在便利性上确实做得很好,几行代码就能对接大模型和向量数据库。但便利性往往伴随着牺牲,它为了让你写得更爽,默认把很多细节藏起来了,比如这次的过滤表达式拼接。
很多开发者(包括我)在用这些高级封装时,很容易产生一种"框架会帮我处理好安全"的错觉。但现实是,框架只提供能力,安全还得靠你自己把关。就像给你一把瑞士军刀,它能开瓶盖也能割手指,关键看你怎么用。
另外,这也暴露了AI应用的一个通病:大家太关注模型效果,忽视了工程安全。咱们都在卷RAG的准确率、Agent的响应速度,结果基础的数据库查询安全都没做好。这次Spring AI的漏洞,说白了就是把传统Web开发时代早就趟过的坑,在AI时代又踩了一遍。
长期防护:建立你的"安全雷达"
1. 订阅官方安全通告
这次漏洞我是从Spring官方安全页面看到的,建议你把它加入浏览器收藏夹,或者订阅RSS。Spring团队处理安全问题的响应速度还是挺快的,从漏洞发现到发布补丁也就几天时间,但你要是不知道,那就等于零。
2. 依赖扫描工具用起来
如果你还没用OWASP Dependency Check或者Snyk这类工具,赶紧安排上。这些工具能自动扫你的依赖库,一旦发现高危CVE会直接报警。比如这次Spring AI的漏洞,这些工具在3月18号之后就已经能识别了。
Maven里配一下,CI/CD流水线跑起来。
3. 最小权限原则
就算没有注入漏洞,你的向量数据库连接账号也不应该有DROP TABLE权限。按照最小权限原则,应用账号只给SELECT和INSERT就够了。这样就算真的被注入,损失也能控制在一定范围内,不至于整个库被拖走。
结语:修完漏洞,记得喝杯咖啡压压惊
写到这看了眼字数,差不多两千字了。其实技术这东西,最怕的不是出问题,而是出了问题你不知道。这次Spring AI的双漏洞虽然评分高,但修复起来还算简单,升个版本号的事,比那些内核级别的漏洞友好多了。
最后提醒一句:升级完之后,记得在测试环境跑一遍你的AI功能,特别是那些依赖元数据过滤的场景。vector store这玩意儿,查询逻辑稍微变一点,召回率可能就掉几个点,安全性和准确性咱们得两头兼顾。
好了,该说的都说了,赶紧去改代码吧。改完记得给自己点杯奶茶,毕竟又阻止了一次潜在的"数据泄露惨案",这杯奶茶你值得拥有。
参考链接
- Spring官方安全通告 CVE-2026-22730
- Spring官方安全通告 CVE-2026-22729
- 阿里云漏洞库详情
- NVD漏洞数据库
(本文技术细节基于2026年3月17-20日公开的安全通告,修复方案已在本地环境验证通过。如有遗漏,欢迎在评论区指正。)
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)