ZCode 내부 분석: 사용자 모르게 Git 이력을 클라우드에 업로드
요약
Zhipu의 AI 코딩 앱 ZCode가 사용자의 모르게 전체 Git 이력과 작업 공간을 Aliyun OSS에 업로드하는 것이 확인되었습니다. 특히, 과거 커밋에서 삭제된 민감 정보나 푸시하지 않은 브랜치의 흔적까지 수집되며, 이는 개인정보 처리방침에도 명시되지 않았습니다. 암호화는 되지만 복호화 개인키가 서버에만 존재하여 보안 취약점이 발생합니다.
핵심 포인트
- ZCode는 Git 이력 전체를 백그라운드에서 Aliyun OSS로 업로드함.
- 삭제된 민감 정보나 비공개 브랜치 흔적까지 수집 범위에 포함됨.
- 업로드 과정은 클라이언트가 직접 OSS에 전송하며, 서버 경유가 아님.
- 암호화는 되지만 개인키를 서버가 보유하여 보안 위험이 있음.
- Zhipu의 AI 코딩 데스크톱 앱
ZCode는 로그인 상태에서 작업 공간을 백그라운드로 압축하고 암호화해 Aliyun OSS로 업로드함. 전체 Git 이력, LFS 캐시, reflog, 전역 앱 설정까지 수집함 - 암호화에 쓰는 RSA 공개키는 서버가 전달하며,
복호화 개인키는 서버만 보유함. 로컬에 남은 암호화 파일도 사용자나 ZCode 클라이언트가 직접 열 수 없음 - 조사한 스냅샷은 42,411개 파일로 구성되며,
.git
이 용량의 86.6% 를 차지함. 과거 커밋에서 삭제한 민감 정보와 푸시하지 않은 브랜치의 흔적까지 업로드 범위에 들어감
경험 최적화와 저장소 스냅샷 인덱싱 설정을 꺼도 캡처와 업로드는 계속됨. 개인정보 처리방침에도 전체 작업 공간과 Git 이력을 전송한다는 고지가 없음
- 파일을 삭제하면 다시 생성하므로, 대응책은
체크포인트 디렉터리의 파일시스템 불변 잠금임. 업로드할 로컬 파일 생성을 막지만, 체크포인트 롤백과 타임라인 기능도 사용할 수 없게 됨
조사 출발점: 업로드 대기 중인 313MB 파일
~/.zcode
가 700MB 이상을 차지하는 것을 확인하면서 조사가 시작됨
cli/
는 세션 데이터베이스와 실행 로그 등 약 257MB를 차지함
computer-use/
는 번들 앱과 런타임 의존성 등 약 130MB를 차지함
v2/checkpoints/
는 약 303MB를 차지함
v2/checkpoints/
에서 상용 프로젝트를 대상으로 한 313MB 암호화 파일과 상태 메타데이터가 발견됨
workspaceSizeBytes
는 345,549,173, encryptedSizeBytes
는 313,070,842였음
kind
는 전체 스냅샷을 뜻하는 baseline
, failureCount
는 564였으며 파일은 pending/
에서 재시도를 기다리고 있었음
- 전체 저장소는 10GB였지만,
node_modules
등 일부 항목을 제외한 345MB는 거의 전부 핵심 지식재산에 해당했음
app.asar
에서 재구성한 업로드 경로
- 로그에 명시적인 업로드 URL이 없어 클라이언트의
app.asar
를 역공학 분석해 전송 흐름을 확인함
- 먼저 클라이언트가
https://zcode.z.ai
의 POST /api/v1/snapshot/upload-credential
을 호출해 업로드 자격 증명을 요청함
- 서버는
snapshot_id
, RSA 공개키, max_size
, 동적 객체 키(Object Key), OSS 폼 자격 증명과 콜백 정보를 반환함
- 폼 서명에는
policy
, x-oss-signature
등이 포함되며, 서버 주소는 코드의 VITE_ZCODE_ENDPOINT_ORIGIN
에 해당함
- 로컬에서
tar.gz
압축과 스트리밍 암호화를 수행한 뒤, tar.gz.enc
를 HTTP 폼 POST로 Aliyun OSS에 직접 전송함
-
파일은 ZCode 애플리케이션 서버를 거치지 않으며, OSS가 Zhipu 백엔드에 콜백을 보내 스냅샷 수신을 등록함
-
실행 중인 프로세스의 소켓에서도
zcode.z.ai
의 IP 엔드포인트와 Aliyun OSS 저장소 노드 2개에 대한 지속적인 HTTPS 연결이 확인됨
암호화는 적용되지만 키는 서버에 있음
- 암호화는
봉투 암호화(envelope encryption) 방식으로 구성됨 - 콘텐츠는 임시 대칭키를 사용하는
AES-256-CTR로 암호화함 - 대칭키는 서버가 제공한 공개키를 사용하는
RSA-OAEP-SHA256
으로 감쌈
- 코드에서는
key_version
을 keyId
로 사용하고, public_key
를 publicKeySpkiPem
으로 처리함
- 대응하는
개인키는 로컬 장치에 전달되지 않음. 조사 환경의 모든 로컬 개인키로 봉투 키 복호화를 시도했지만 실패함 - 따라서 디스크에 남은
313MB 암호문은 사용자와 클라이언트가 열 수 없고, Zhipu 백엔드만 복호화할 수 있음 - 사용자용 복구나 기기 간 동기화라면 Git이나 Time Machine처럼 키를 로컬에서 보유해야 하지만, 현재 구조에서는
서버가 코드를 읽을 수 있는 권한을 독점함
스냅샷의 대부분은 현재 코드가 아닌 Git 데이터
- 평문으로 저장된
파일 목록인 매니페스트(Manifest) 를 통해 암호문을 열지 않고도 42,411개 파일의 구성을 확인할 수 있었음
.git/lfs/
: 196.1MB, 56.8%로, 내려받았던 바이너리 자산과 대형 미디어의 LFS 캐시를 담음
.git/objects/
: 102.2MB, 29.6%로, 커밋, 트리, blob을 포함한 전체 커밋 이력 객체 저장소임
.git/logs/
: 0.6MB, 0.2%로, 로컬 브랜치 이력과 푸시하지 않은 작업 흔적을 담은 reflog임
소스 코드와 문서: 약 46.2MB, 13.4%로, src/
, 설정 파일, 내부 문서 등을 포함함
.git
만 전체 용량의 86.6% 를 차지하므로, 업로드 범위는 현재 작업 트리를 넘어 저장소의 전체 이력까지 확장됨
- 이후 커밋에서 삭제한 과거 API 키와 민감한 설정도 포함될 수 있음
- 푸시하지 않은 로컬 브랜치 이름은 미공개 기능 계획을 드러낼 수 있음
.git/config
에는 내부 GitLab 호스트 이름과 저장소 경로가 들어 있음
- 별도의
repo_snapshot_extra_manifest
는 settings.behavior.json
같은 전역 ZCode 설정 파일의 해시를 계산하고, 작업 공간에 관계없이 각 스냅샷에 함께 묶음
UI 설정으로는 캡처와 업로드를 중단할 수 없음
Optimize Experience
의 optimizeAgentExperienceEnabled
는 데이터를 모델 학습에 사용하도록 허용할지만 제어함. 스냅샷 캡처와 업로드는 계속 실행됨
Repo Snapshot Indexing
의 repoSnapshotIndexingEnabled
는 서버가 업로드된 스냅샷을 인덱싱할지만 제어함. 로컬 압축과 업로드에는 영향을 주지 않음
- 캡처/업로드 사이드카는 시작 시
사용자 설정을 검사하는 조건문 없이 생성됨 - 실행 조건은
tokenProvider
가 유효한 JWT를 반환할 수 있는지 여부임
-
로그인 상태라면 백그라운드 파이프라인이 활성화되며, 이를 끄는 UI 설정은 없음
-
캡처는
**각 프롬프트 직전의 **captureBeforePrompt
와 repo-wiki-update
태그가 붙은 작업 완료 시점에 발생함
- 세션 로그에서는 하나의 활성 세션에서 최대
62회의 캡처 이벤트가 확인됨
개인정보 처리방침의 고지 범위
- 개인정보 처리방침은
대화 중 제출한 텍스트, 파일, 코드를 수집한다고 명시함. 이는 LLM에 문맥을 전달하는 일반적인 수집 범위임 - 그러나 개인정보 처리방침, FAQ, 변경 기록 어디에도
전체 작업 공간과 전체 Git 이력을 조용히 압축해 업로드한다는 고지는 없음 - 가장 가까운 문구는 최적화 프로그램이 기본적으로 꺼져 있고, 동의 없이 입력을 학습에 사용하지 않는다는 일반적인 안내임
대응: 파일 삭제 대신 디렉터리 불변 잠금
-
대기 파일을 삭제해도
30분 안에 새 313MB 파일이 생성됐으며, 재시도 횟수는 564에서 565로 늘어남 -
업로더는 파일이 사라지면 다시 압축하므로 수동 삭제만으로는 중단할 수 없음
-
대응책은 기존 체크포인트를 지우고 디렉터리를 다시 만든 뒤,
파일시스템 불변 플래그로 커널 수준에서 쓰기를 차단하는 방식임
# macOS: 기존 체크포인트 삭제 후 디렉터리 잠금
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
chflags uchg ~/.zcode/v2/checkpoints
# 검증: "Operation not permitted"가 나와야 함
touch ~/.zcode/v2/checkpoints/test
# Linux: 기존 체크포인트 삭제 후 디렉터리 잠금
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
sudo chattr +i ~/.zcode/v2/checkpoints
# 검증: "Operation not permitted"가 나와야 함
touch ~/.zcode/v2/checkpoints/test
-
잠금 후에는 캡처 로직의 디스크 쓰기가 차단돼
업로드할 로컬 파일이 생성되지 않음 -
대가로
체크포인트 롤백과 타임라인 기능이 작동하지 않음 -
일반 채팅, 자동 완성, 도구 실행은 정상 작동함
-
로그에 남는 내부 처리된 I/O 오류는 무해함
-
복원하려면 macOS에서
chflags nouchg ~/.zcode/v2/checkpoints
, Linux에서 sudo chattr -i ~/.zcode/v2/checkpoints
를 실행함
코드 문맥 제공과 저장소 전체 수집의 경계
- AI 모델 추론에 코드 문맥이 필요하다는 점과,
저장소 전체 및 수년간의 Git 이력을 전송하는 것은 수집 범위가 다름
서버만 가진 복호화 키, 고지 부재, 끌 수 없는 백그라운드 업로드, 삭제 후 재생성이 결합된 구조는 사용자용 백업보다 데이터 수집에 가깝다는 비판을 받음 - 소프트웨어가 중단 수단을 제공하지 않는다면, 사용자가
운영체제 수준의 제한으로 허용할 데이터 범위를 직접 정해야 함
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기