Files
a301_launcher/ANALYSIS.md
tolelom 48df55a82e fix: 보안 강화, 안정성 및 UX 개선
- 토큰 전달 방식 변경: 명령줄 인자(-token) → 환경변수(A301_TOKEN)로 프로세스 목록 노출 방지
- 고정 설치 경로: %LOCALAPPDATA%\A301\로 런처 복사 후 레지스트리 등록 (Downloads 정리 시 깨짐 방지)
- zip 추출 시 symlink 엔트리 스킵 (경로 탈출 방지)
- fetchServerInfo 3회 재시도 (exponential backoff)
- 다운로드 이어받기: Range 헤더 지원, 취소/오류 시 임시 파일 유지
- 416 응답 시 서버 파일 변경 감지하여 처음부터 재다운로드
- 단일 인스턴스 UX: 기존 창 FindWindow+SetForegroundWindow로 활성화
- uninstall 시 설치 디렉토리 정리

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:10:11 +09:00

11 KiB

A301 시스템 통합 분석 및 개선 계획

분석일: 2026-03-06 대상: 런처(Go) / 서버(Go+Fiber) / 웹 클라이언트(React+Vite)


1. 시스템 전체 흐름

[웹 클라이언트]                [서버]                    [런처]                [게임 클라이언트]
     |                          |                         |                        |
     |-- POST /auth/login ----->|                         |                        |
     |<-- {token, refresh} -----|                         |                        |
     |                          |                         |                        |
     |-- GET /download/info --->|                         |                        |
     |<-- {url,hash,version} ---|                         |                        |
     |                          |                         |                        |
     |== a301://launch?token= =|========================>|                        |
     |                          |                         |                        |
     |                          |<-- GET /download/info --|                        |
     |                          |-- {url,hash,version} -->|                        |
     |                          |                         |-- SHA256 비교           |
     |                          |<-- GET /download/file --|  (불일치시)             |
     |                          |-- game.zip ------------>|                        |
     |                          |                         |-- 압축 해제             |
     |                          |                         |-- A301.exe -token xxx ->|
     |                          |                         |                        |
     |                          |<--- /internal/chain ----|-- 게임 내 API 호출 ---->|

2. 발견된 버그 및 문제점

2.1 [RESOLVED] 런처: Zip Slip 경로 검증 로직

재검증 결과 로직이 정상임. !A && B는 De Morgan 법칙에 의해 "descendant이거나 self"일 때만 허용하는 올바른 조건.


2.2 [RESOLVED] 서버: /auth/verify Redis 세션 검증

재검증 결과 service.go:212-216에서 이미 Redis 세션을 확인하고 있음.


2.3 [HIGH] 런처: 서버 fileHash 비어있으면 검증 우회

위치: launcher/main.go ensureGame() 문제: serverInfo.FileHash == ""이면 해시 비교를 건너뛰고 기존 파일을 그대로 사용. 서버에서 zip에 A301.exe가 없으면 fileHash가 빈 문자열로 저장됨.

연쇄 시나리오:

  1. 관리자가 A301.exe 없는 zip 업로드 → 서버 fileHash = ""
  2. 런처가 info 조회 → hash 빈 문자열 → 검증 스킵
  3. 로컬 A301.exe가 어떤 파일이든 실행됨

수정안:

  • 서버: zip에 A301.exe 없으면 업로드 거부 (현재는 빈 해시 허용)
  • 런처: fileHash가 비어있으면 에러 처리

심각도: HIGH (보안/무결성)


2.4 [HIGH] 런처: 토큰이 프로세스 명령줄에 노출

위치: launcher/main.go handleURI() 문제: A301.exe -token <JWT>로 실행 → tasklist, Process Explorer 등에서 토큰 노출

수정안:

  • 환경변수로 전달: os.Setenv("A301_TOKEN", token) 후 실행
  • 또는 임시 파일에 토큰 쓰고 파일 경로 전달

심각도: HIGH (보안)


2.5 [HIGH] 블록체인 트랜잭션 멱등성 미보장

위치: server/internal/chain/service.go 모든 트랜잭션 함수 문제: 동일 요청 재전송 시 중복 처리 (토큰 이중 전송, NFT 이중 민팅)

  • 네트워크 타임아웃 후 클라이언트 재시도 시 발생 가능

수정안:

  • 클라이언트에서 idempotency key 전송
  • 서버에서 Redis로 중복 요청 감지 (TTL 기반)

심각도: HIGH (데이터 무결성)


2.6 [HIGH] 서버: Rate Limiting 없음

위치: 전체 API 문제: 토큰 리프레시, 로그인, 다운로드 등 모든 엔드포인트에 속도 제한 없음.

  • 브루트포스 로그인 공격 가능
  • 리프레시 토큰 재사용 공격 가능
  • 다운로드 대역폭 남용 가능

수정안: Fiber 미들웨어로 rate limiter 추가 (IP 기반 + 토큰 기반)

심각도: HIGH (보안)


2.7 [MEDIUM] 웹 클라: 관리자 권한 클라이언트 사이드만 검증

위치: client/src/components/AdminRoute.jsx 문제: localStorage의 role을 조작하면 관리자 UI 접근 가능. API 호출은 서버에서 차단되지만, UI 자체가 노출됨.

영향: 관리자 기능 목록/구조가 일반 사용자에게 보임

수정안: 서버 API가 이미 권한 체크하므로 실질적 위험은 낮음. 다만 관리자 라우트 로딩 시 서버에서 role 재확인하면 더 안전.

심각도: MEDIUM (UX/보안)


위치: launcher/main.go extractZip() 문제: zip 내 심볼릭 링크를 통한 디렉토리 탈출 미검증

  • filepath.Clean()은 심볼릭 링크를 해석하지 않음

수정안: zip 엔트리의 파일 모드에서 symlink 비트 확인 → 거부

심각도: MEDIUM (보안)


2.9 [MEDIUM] 런처: 네트워크 재시도 로직 없음

위치: launcher/main.go fetchServerInfo(), doDownload() 문제: 일시적 네트워크 오류 시 즉시 실패. 다운로드 99%에서 끊기면 처음부터 재시작.

수정안:

  • fetchServerInfo: 3회 재시도 (exponential backoff)
  • doDownload: Range 헤더 지원으로 이어받기 구현

심각도: MEDIUM (안정성/UX)


2.10 [MEDIUM] 런처: 단일 인스턴스 체크 시 사용자 피드백 없음

위치: launcher/main.go main() mutex 체크 문제: 이미 실행 중이면 조용히 종료 → 사용자는 클릭했는데 아무 반응 없음

수정안: 기존 인스턴스 창을 foreground로 가져오기 (FindWindow + SetForegroundWindow)

심각도: MEDIUM (UX)


2.11 [MEDIUM] 웹 클라: 다운로드 정보 실패 시 무음 처리

위치: client/src/components/DownloadSection.jsx 문제: /api/download/info 실패 시 console.error도 없이 "런처 준비 중" 메시지 표시. 디버깅 불가.

수정안: 에러 로깅 추가 + 재시도 버튼 제공

심각도: MEDIUM (UX/디버깅)


2.12 [MEDIUM] 서버: 파일 업로드 크기 제한 없음

위치: server/internal/download/handler.go 문제: StreamRequestBody 활성화되어 있지만 별도 크기 제한 없음. 수십 GB 파일 업로드 시 디스크 고갈 가능.

수정안: Fiber의 BodyLimit 미들웨어 적용 (예: 4GB)

심각도: MEDIUM (안정성)


2.13 [LOW] 서버: Username 대소문자 구분

위치: server/internal/auth/model.go 문제: "User"와 "user"가 별도 계정으로 등록 가능. 블록체인 username 조회 시 혼동 가능.

수정안: 등록/로그인 시 username을 항상 소문자로 정규화

심각도: LOW (데이터 일관성)


2.14 [LOW] 서버: Wallet 암호화 키 시작 시 미검증

위치: server/internal/chain/service.go 문제: WALLET_ENCRYPTION_KEY가 잘못되면 런타임에 지갑 복호화 실패. 서버 시작 시 검증하지 않음.

수정안: 서비스 초기화 시 키 길이/형식 검증 (64 hex = 32 bytes) → fail fast

심각도: LOW (운영 안정성)


2.15 [LOW] 웹 클라: 비밀번호 검증이 약함

위치: client/src/pages/RegisterPage.jsx 문제: 6자 이상만 체크. 복잡도 요구 없음.

수정안: 서버 측 검증에 의존하더라도, 클라이언트에서도 실시간 피드백 제공

심각도: LOW (보안/UX)


3. 통합 흐름 검증 결과

3.1 정상 시나리오 (Happy Path) - OK

단계 동작 상태
웹 로그인 POST /auth/login → JWT 발급 OK
다운로드 정보 GET /download/info → hash/url/version OK
런처 호출 a301://launch?token=xxx OK
런처 서버 조회 GET /download/info OK
해시 비교 로컬 A301.exe SHA256 vs 서버 fileHash OK
다운로드 GET /download/file → zip OK
압축 해제 extractZip → moveContents OK
게임 실행 A301.exe -token xxx OK
게임→서버 /internal/chain/* (X-API-Key) OK

3.2 엣지 케이스 검증

시나리오 기대 동작 실제 동작 판정
서버 다운 시 런처 호출 에러 표시 MessageBox 표시 후 종료 OK
토큰 만료 후 게임 시작 리프레시 후 전달 웹 클라: localStorage에서 읽음 (최신 토큰) PARTIAL - 리프레시 타이밍에 따라 만료 토큰 전달 가능
다운로드 중 네트워크 끊김 재시도 또는 복구 즉시 실패, 재시작 필요 FAIL
동시에 런처 2개 실행 하나만 실행 Mutex로 차단, 피드백 없음 PARTIAL
A301.exe 없는 zip 업로드 업로드 거부 fileHash="" 로 저장 → 업로드 거부 (수정됨) FIXED
로그아웃 후 토큰으로 verify 무효 판정 Redis 세션 확인으로 무효 판정 (기존 정상) OK
관리자가 대용량 파일 업로드 크기 제한 제한 없음 FAIL
블록체인 트랜잭션 중복 요청 한 번만 처리 중복 처리됨 FAIL

4. 개선 우선순위 로드맵

Phase 1: 보안 긴급 패치

# 항목 대상 상태
1 Zip Slip 검증 로직 수정 런처 재검증: 정상
2 /verify Redis 세션 확인 추가 서버 재검증: 이미 구현됨
3 fileHash 빈 문자열 시 업로드 거부 서버 완료
4 토큰 전달 방식 변경 (명령줄 → 환경변수) 런처 완료
5 Rate limiting 추가 서버 완료
6 Symlink 차단 런처 완료

Phase 2: 안정성 개선

# 항목 대상 상태
7 네트워크 재시도 로직 (fetchServerInfo) 런처 완료
8 블록체인 트랜잭션 멱등성 키 서버 완료
9 파일 업로드 크기 제한 서버 완료
10 런처 단일 인스턴스 UX 개선 런처 완료

Phase 3: UX 개선

# 항목 대상 상태
11 다운로드 정보 실패 시 재시도 버튼 웹 클라 완료
12 비밀번호 복잡도 실시간 피드백 웹 클라 완료
13 Username 대소문자 정규화 서버 완료
14 Wallet 암호화 키 시작 시 검증 서버 이미 구현됨
15 다운로드 이어받기 (Range 헤더) 런처 완료

5. 컴포넌트별 요약

런처 (Go/Win32)

  • 강점: DPI 인식, 원자적 파일 이동, 다운로드 진행 UI, 2GB 크기 제한
  • 약점: Zip Slip 검증 오류, 토큰 노출, 재시도 없음, symlink 미차단

서버 (Go/Fiber)

  • 강점: 모듈화된 구조, JWT+Redis 세션, AES-256 지갑 암호화, 원자적 파일 업로드
  • 약점: verify 세션 미검증, rate limiting 없음, 트랜잭션 멱등성 없음, 빈 해시 허용

웹 클라이언트 (React/Vite)

  • 강점: 토큰 자동 리프레시, 관리자 UI 분리, 다크 테마, SPA 라우팅
  • 약점: 에러 피드백 부족, 관리자 클라이언트 사이드만 검증, 약한 비밀번호 정책