☰
AI 写的上传接口,我一眼看出三个漏洞——Trae 边界实录(中):安全与隐式耦合
2026/10/24 4:10:07 网站建设 项目流程

系列第 2 篇。上篇讲业务、版本、架构;本篇讲安全与隐式耦合;下篇讲协作与一致性。

开场:最危险的,不是“报错”

让 Trae 写一个文件上传接口,Prompt 是:

写一个 FastAPI 文件上传接口,把文件保存到 uploads 目录。

它给出:

fromfastapiimportFastAPI,UploadFileimportos app=FastAPI()@app.post("/upload")asyncdefupload(file:UploadFile):content=awaitfile.read()save_path=os.path.join("./uploads",file.filename)withopen(save_path,"wb")asf:f.write(content)return{"ok":True,"path":save_path}

跑得通。上传个小图片,返回{"ok": true},一切正常。

这就是最危险的地方——它“能用”,所以你会直接上生产。

下面三个漏洞,全部可复现。


漏洞一:路径穿越

file.filename是客户端可控的。构造一个请求:

curl-XPOST http://localhost:8000/upload\-F"file=@test.txt;filename=../../etc/cron.d/evil"

os.path.join("./uploads", "../../etc/cron.d/evil")会拼成:

>>>os.path.join("./uploads","../../etc/cron.d/evil")'uploads/../../etc/cron.d/evil'

os.path.join不会清理..,实际写到/etc/cron.d/evil。配合定时任务,直接 RCE。


漏洞二:类型伪造

curl-XPOST http://localhost:8000/upload\-F"file=@shell.php;filename=shell.php"

上传一个.php,如果 uploads 目录被 Web 服务器当静态目录暴露,就是 webshell。Trae 的原始代码完全不看文件类型。


漏洞三:内存 DoS

ddif=/dev/urandomof=big.binbs=1Mcount=10240curl-XPOST http://localhost:8000/upload-F"file=@big.bin"

await file.read()把整个文件读进内存。10GB 文件直接打爆进程,服务挂掉——一个极低成本的 DoS。


修复版(可运行)

fromfastapiimportFastAPI,UploadFile,HTTPExceptionfrompathlibimportPurePosixPathimportos,uuid app=FastAPI()UPLOAD_DIR="./uploads"MAX_SIZE=10*1024*1024# 10MBALLOWED_EXT={".png",".jpg",".jpeg",".gif",".pdf"}os.makedirs(UPLOAD_DIR,exist_ok=True)@app.post("/upload")asyncdefupload(file:UploadFile):# 1. 类型校验:扩展名白名单ext=PurePosixPath(file.filenameor"").suffix.lower()ifextnotinALLOWED_EXT:raiseHTTPException(400,"不支持的文件类型")# 2. 重命名,彻底杜绝路径穿越safe_name=f"{uuid.uuid4().hex}{ext}"save_path=os.path.join(UPLOAD_DIR,safe_name)# 3. 分块读取,超限中断size=0withopen(save_path,"wb")asf:whilechunk:=awaitfile.read(1024*1024):size+=len(chunk)ifsize>MAX_SIZE:f.close()os.remove(save_path)raiseHTTPException(413,"文件过大")f.write(chunk)return{"ok":True,"path":save_path}

修复点对照:

漏洞修复手段
路径穿越不用原始文件名,uuid重命名 + 扩展名单独校验
类型伪造扩展名白名单,后续可加 MIME / 文件头校验
内存 DoS分块读取,超限中断并删除残留文件

安全追问必须由你发起

Trae 默认只回答“怎么实现”,不回答“怎么防人”。所以正确的用法是:让它先写,然后你明确追问——

这段代码存在哪些安全问题?请逐一列出: 1. 输入校验 2. 路径/权限 3. 资源耗尽 4. 注入 每一类都给出攻击方式与修复代码。

它会补得不错。但触发这个追问的,必须是你,不能是它。安全的责任,AI 不会主动接。


延伸坑:跨文件隐式耦合

安全之外,还有一类更隐蔽的漏洞:

# 直接调用(Trae 能看到)utils.format_price(x)# 通过字符串反射调用(Trae 看不到)handler=getattr(utils,"format_price")handler(x)

让 Trae 改format_price的签名,它会改掉可见的直接调用,却漏掉getattr这种字符串引用。上线后反射调用报错。

根因:Trae 按静态调用关系判断影响面,对字符串、反射、事件回调注册、配置里的类名识别不可靠。

改进方法:改公共 API 前自己 grep 全仓库:

grep-rn"format_price"--include="*.py".

再确认有没有字符串、反射、配置用法。把 AI 的“影响面分析”当线索,不当结论。


小结

  • Trae 写的上传接口,逻辑“无懈可击”,安全“千疮百孔”
  • 安全追问必须由你发起
  • 改公共 API,全仓 grep 不能省—

上篇:业务、版本、架构(见系列上篇)。
下篇:为什么 AI 写的 review 没人信,以及怎么让 AI 长期保持一致。收藏 + 关注,系列不丢。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询