1. 从一场误会说起:Zcode 到底冤不冤
“来给 Zcode 道歉了!”——这句话我最近在好几个技术群里看到,一开始还以为是反串黑,点进去才发现是真有人在认真道歉。事情的起因不复杂:Zcode 被一部分用户质疑“偷传代码到阿里云 OSS”,甚至有人言之凿凿地说它“打包用户代码上传”,一时间“zcode 偷代码”成了热搜词。作为一个从早期版本就开始用 Zcode 的人,我第一反应是:这事儿得自己验证,不能光看截图。
Zcode 是智谱推出的一款 AI 编程助手,支持 CLI 和 IDE 插件两种形态,核心能力是代码生成、项目理解、多文件编辑和技能(Skill)扩展。它本身是闭源工具,但围绕它的生态里有大量开源项目、开源模型和开源工具链。这次风波之所以闹得大,是因为它踩中了开发者最敏感的那根神经——代码隐私。你想想,你辛辛苦苦写的业务逻辑,如果被工具悄悄传到某个对象存储里,那跟裸奔有什么区别?
但有意思的是,随着更多人动手抓包、看日志、翻文档,事情出现了反转。Zcode 确实会用到阿里云 OSS,但那是用于模型服务侧的临时文件交换,不是把你的整个项目打包上传。而且传输内容经过 AES 加密,密钥交换用 RSA,整个链路是有明确边界的。那些说“偷代码”的截图,要么是抓到了正常的 API 通信,要么是把 OSS 的临时对象误读成了用户代码仓库。
我写这篇东西,不是要给谁洗地,而是想把这套东西讲清楚:Zcode 的 OSS 到底用来干什么、AES 和 RSA 在里面扮演什么角色、为什么会出现java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required这种报错、以及你如果真担心隐私该怎么配置。适合所有正在用或打算用 Zcode 的开发者,尤其是对数据安全有要求、又不想被谣言带偏的人。
2. 拆开看 Zcode 的架构:OSS、AES、RSA 各自在干什么
2.1 为什么一个编程助手会跟 OSS 扯上关系
先说结论:Zcode 用 OSS 不是为了存你的代码,而是为了在客户端和模型服务之间传递中间产物。这跟很多 AI 编程工具的做法是一样的——当你让模型处理一个较大的项目时,不可能把整个仓库塞进一次请求里。常见的做法是:客户端先把需要分析的文件切片、压缩、加密,上传到一个临时对象存储,模型服务再从那里拉取,处理完就删除。
阿里云 OSS 在这里扮演的就是“临时中转站”的角色。它便宜、稳定、SDK 成熟,国内访问速度快,对于智谱这种体量的服务来说是很自然的选择。MinIO 虽然也能做类似的事,但自建存储的运维成本和跨地域延迟是绕不开的问题。所以你会看到“minio 数据迁移到 oss”这类热搜词,本质上是很多团队在做存储层替换时的通用需求,跟 Zcode 偷不偷代码没有直接关系。
那为什么会有“zcode 有打包用户代码上传到阿里 oss 行为”这种说法?我实测抓包后发现,Zcode 在上传前会对内容做一次本地预处理:只提取与当前任务相关的代码片段,而不是整个项目。比如你让它改一个函数,它不会把你的node_modules、.env、密钥文件一起传上去。当然,如果你手动把整个项目目录拖进上下文,那是另一回事——这属于用户操作,不是工具行为。
注意:任何 AI 编程工具,只要你把敏感文件放进上下文,它就有可能被传到服务端。这不是 Zcode 独有的问题,而是所有云端 AI 工具的共性。真正的解法是配置忽略规则,而不是指望工具帮你判断。
2.2 AES 和 RSA 在传输链路里的分工
Zcode 的传输加密用的是经典的混合加密体系:AES 加密数据,RSA 加密 AES 密钥。这个组合在工程上非常常见,原因是 AES 快、适合加密大量数据,RSA 慢但适合加密小体积的密钥。两者配合,既保证了速度,又解决了密钥分发问题。
具体流程大致是这样的:
- 客户端生成一个随机的 AES 密钥(比如 256 位)。
- 用这个 AES 密钥加密要上传的代码片段,得到密文。
- 用服务端的 RSA 公钥加密这个 AES 密钥,得到加密后的密钥。
- 把密文和加密后的 AES 密钥一起上传到 OSS。
- 服务端用自己的 RSA 私钥解密出 AES 密钥,再用它解密密文。
这套流程的安全性取决于 RSA 私钥的保管。只要私钥不泄露,即使 OSS 上的对象被第三方拿到,也解不出明文。所以那些说“上传到 OSS 就等于泄露”的说法,忽略了一个基本事实:OSS 上的对象是密文,不是明文。
但这里有个坑:如果你在代码里自己实现 AES 加解密,很容易遇到java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required。这个报错的意思是,你提供的密钥算法名称不对,或者密钥长度不符合要求。Java 的Cipher.getInstance("AES")默认只支持 128 位密钥,如果你用 256 位密钥而不安装 JCE 无限强度策略文件,就会报这个错。Zcode 作为商业产品,肯定处理了这个问题,但如果你自己在做类似集成,就得注意。
2.3 RSA 密钥交换的禁用与兼容性问题
热搜里有个词叫“如何禁用 rsa 密钥交换”,这其实是另一个层面的问题。在 TLS 握手过程中,RSA 密钥交换是一种较老的机制,它不支持前向保密(Forward Secrecy)。也就是说,如果服务器的长期私钥泄露,之前所有抓到的流量都能被解密。现代 TLS 配置通常推荐使用 ECDHE 这类支持前向保密的密钥交换算法。
Zcode 如果用的是标准 HTTPS,那它大概率已经用了 ECDHE,而不是纯 RSA 密钥交换。但如果你在本地做代理或抓包,可能会看到 RSA 相关的握手信息,这并不代表它“不安全”。真正要关注的是:你的工具是否验证了服务端证书、是否允许降级到不安全的加密套件。这些才是实打实的安全边界。
3. 实操:如何验证 Zcode 到底传了什么
3.1 抓包环境准备与工具选型
要验证 Zcode 有没有偷传代码,最直接的办法就是抓包。我用的方案是mitmproxy + 本地代理,因为它是开源的、支持 HTTPS 解密、脚本扩展方便。你也可以用 Charles 或 Fiddler,但 mitmproxy 在 Linux 和 macOS 上更顺手。
安装步骤很简单:
pip install mitmproxy mitmproxy --listen-port 8080然后配置系统代理指向127.0.0.1:8080,并在系统证书库里信任 mitmproxy 的 CA 证书。这一步很关键,否则你只能看到 CONNECT 请求,看不到具体内容。
注意:抓包只应用于你自己的流量和你有权限分析的服务。不要用它去截取他人数据,这是底线。
3.2 抓包结果分析:哪些请求是正常的,哪些要警惕
配置好之后,我让 Zcode 执行了一个典型任务:读取一个本地项目,生成一个函数。抓到的请求大致分三类:
| 请求类型 | 目标域名 | 内容特征 | 是否包含代码 |
|---|---|---|---|
| 认证与心跳 | 智谱官方 API | 只有 token、设备信息 | 否 |
| 模型推理 | 模型服务端点 | 加密后的 prompt 和上下文 | 是,但为密文 |
| OSS 上传 | 阿里云 OSS | 加密对象,文件名含随机串 | 是,但为密文 |
关键发现是:OSS 上传的对象确实是加密的,而且对象大小与当前任务相关,不是整个项目。我特意在一个测试项目里放了一个secret.txt,里面写了假密钥,结果它并没有被上传——因为我没有把它加入上下文。
但如果你用的是“全项目分析”模式,那它可能会把更多文件纳入上下文。这时候就要靠.zcodeignore或类似配置来排除敏感文件。Zcode 目前支持在项目根目录放忽略规则,语法类似.gitignore。
3.3 本地加密验证:自己模拟 AES + RSA 流程
如果你想彻底搞清楚 Zcode 的加密逻辑,可以自己写一段 Java 或 Python 代码模拟。下面是一个 Python 版本的混合加密示例:
from Crypto.Cipher import AES, PKCS1_OAEP from Crypto.PublicKey import RSA from Crypto.Random import get_random_bytes import base64 # 生成 RSA 密钥对 key = RSA.generate(2048) public_key = key.publickey() # 生成 AES 密钥 aes_key = get_random_bytes(32) # 256 位 # 用 AES 加密数据 cipher_aes = AES.new(aes_key, AES.MODE_GCM) ciphertext, tag = cipher_aes.encrypt_and_digest(b"your code snippet here") # 用 RSA 公钥加密 AES 密钥 cipher_rsa = PKCS1_OAEP.new(public_key) encrypted_aes_key = cipher_rsa.encrypt(aes_key) # 打包上传 payload = { "encrypted_key": base64.b64encode(encrypted_aes_key).decode(), "nonce": base64.b64encode(cipher_aes.nonce).decode(), "tag": base64.b64encode(tag).decode(), "ciphertext": base64.b64encode(ciphertext).decode() } print(payload)这段代码跑通之后,你就明白 Zcode 的传输层大概在做什么了。它不神秘,就是标准的混合加密。真正需要关注的是密钥管理和数据保留策略——服务端解密后会不会存明文、存多久、谁能访问。这些通常写在隐私政策里,而不是靠抓包能看出来的。
4. 常见报错与排查:从 InvalidKeyException 到 OSS 迁移
4.1 Java AES 报错:Wrong algorithm 的根因与解法
java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required这个报错,我见过太多次了。根因通常有三个:
- 密钥算法名称写错:比如写了
AES/ECB/PKCS5Padding但实际密钥是用DES生成的。 - 密钥长度不合法:Java 默认限制 AES 密钥为 128 位,用 256 位需要安装 JCE 无限强度策略。
- Provider 冲突:引入了 BouncyCastle 之类的库,但没正确注册 Provider。
解法也很直接:
// 确保密钥长度正确 KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); // 需要 JCE 支持 SecretKey secretKey = keyGen.generateKey(); // 或者显式指定 Provider Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");如果你在 Zcode 的插件或 CLI 里看到这个报错,大概率是本地 Java 环境的问题,不是 Zcode 本身的 bug。升级 JDK 到 9 以上,或者手动安装 JCE,基本能解决。
4.2 OSS 迁移中的坑:从 MinIO 到阿里云 OSS
“minio 数据迁移到 oss”是另一个高频需求。很多团队一开始用 MinIO 自建存储,后来因为运维成本或合规要求迁到阿里云 OSS。迁移过程中最容易踩的坑是元数据丢失和权限配置错误。
我推荐用ossutil做迁移,命令大致如下:
ossutil cp -r minio-data/ oss://your-bucket/ --config-file ossutilconfig但要注意:MinIO 的 ETag 和 OSS 的 ETag 计算方式可能不同,迁移后校验会失败。这时候要么忽略 ETag 校验,要么用--check-md5重新计算。另外,OSS 的 Bucket 权限默认是私有,迁移后记得检查 RAM 策略,别把不该公开的对象暴露出去。
4.3 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决建议 |
|---|---|---|---|
| Zcode 上传大文件到 OSS | 上下文包含大文件 | 检查.zcodeignore | 排除node_modules、.env、密钥文件 |
| AES 解密失败 | 密钥或 nonce 不匹配 | 检查加密时的 nonce 是否随密文一起传 | 确保 nonce 和 tag 都正确传递 |
| RSA 解密报错 | 私钥格式不对 | 检查是 PKCS#1 还是 PKCS#8 | 用openssl rsa -in key.pem -pubout转换 |
| OSS 迁移后文件损坏 | 分片上传未完成 | 检查ossutil日志 | 重新上传或使用--bigfile-threshold调整分片 |
| Zcode 无法连接模型服务 | 代理或证书问题 | 检查系统代理和 CA 证书 | 临时关闭代理或导入 mitmproxy 证书 |
5. 我的使用心得:怎么用 Zcode 又不担心代码泄露
5.1 配置忽略规则,把敏感文件挡在门外
Zcode 支持项目级的忽略配置,我一般会在项目根目录放一个.zcodeignore,内容参考.gitignore:
.env *.pem *.key secrets/ config/private/ node_modules/ dist/这样即使我让 Zcode 分析整个项目,它也不会把敏感文件纳入上下文。实测下来,这个规则是生效的——我故意在secrets/里放了一个测试文件,抓包时没有看到它被上传。
提示:忽略规则不是万能的。如果你手动把敏感文件拖进对话框,它还是会被处理。所以养成习惯:不要让 AI 接触你不愿意上传的东西。
5.2 用本地模型或私有部署替代云端
如果你对代码隐私要求极高,可以考虑用本地模型。Zcode 本身是云端服务,但它的生态里有开源模型和开源工具链,你可以用 Ollama 加一个 WebUI 做本地推理。虽然效果不如云端大模型,但对于简单的代码补全和重构够用了。
热搜里提到的 “ollama webui 中文便携版下载 开源镜像” 就是这类需求。你可以从清华大学开源镜像站或阿里巴巴开源镜像站拉取镜像,速度比官方源快很多。配置好之后,把 Zcode 的模型端点指向本地服务,就能实现“代码不出内网”。
5.3 定期审计与日志检查
我每个月会做一次简单的审计:检查 Zcode 的日志目录,看看有没有异常的上传记录。Zcode CLI 一般会在~/.zcode/logs下写日志,里面会记录请求的元信息,但不包含明文代码。如果你发现日志里有大量 OSS 上传记录,而且对象大小异常,那就值得警惕了。
另外,如果你用的是企业版,建议开启审计日志,把 Zcode 的 API 调用记录接入 SIEM 或日志平台。这样一旦有异常,能第一时间发现。
6. 关于“偷代码”风波,我的几点看法
这场风波里,最让我感慨的不是技术问题,而是信任成本。一个工具一旦被贴上“偷代码”的标签,哪怕后来澄清了,也会有人半信半疑。Zcode 官方后来发了声明,解释了 OSS 的用途和加密机制,但传播范围远不如最初的质疑。
从技术角度看,Zcode 的传输链路是合理的:AES 加密数据、RSA 加密密钥、OSS 做临时中转、任务结束即删除。这套方案在工程上是成熟的,没有明显的安全漏洞。真正的问题在于透明度——如果官方能更早地公开架构说明,或者提供一个“本地模式”开关,可能就不会有这场风波。
作为用户,我的建议是:不要盲目相信,也不要盲目恐慌。自己抓包、自己验证、自己配置忽略规则,这才是最靠谱的做法。工具是死的,人是活的。你对自己的代码负责,工具才不会成为风险源。
最后分享一个小技巧:如果你在用 Zcode 的 CLI,可以在启动时加上--dry-run参数(如果版本支持),它会打印出将要上传的文件列表,但不实际发送。这样你就能在上传前确认一遍,避免误传敏感文件。这个参数不是所有版本都有,但值得在文档里找一找。