바이낸스 API 키 유출은 계정 탈취 사건의 3대 원인 중 하나입니다. 코드를 GitHub에 푸시하거나, 스크린샷을 커뮤니티에 공유하거나, JSON 파일에 평문으로 저장하거나, 퇴사자가 자격 증명을 가져가는 경우 등이 이에 해당합니다. 보안의 핵심은 절대 코드에 키를 작성하지 않는 것, 정적 저장 시 암호화하는 것, 프로세스 실행 시에만 복호화하여 로드하는 것, 그리고 정기적인 로테이션입니다. 이 글에서는 6가지 주요 저장 솔루션과 전체 코드 예제를 다룹니다. 설정 전 바이낸스 공식 사이트의 API 관리에서 적절한 권한의 키를 생성하세요. 바이낸스 공식 앱을 다운로드하면 생성된 키 목록을 언제든지 확인할 수 있습니다. 이 가이드는 유출 원인, 환경 변수, 키 관리자, 클라우드 Secrets, 로테이션 전략, IP 화이트리스트 연동, 감사 로그, 응급 대응의 8단계로 구성됩니다.
1. 왜 평문 저장이 자산 손실로 이어지는가
주요 유출 경로 통계 (커뮤니티 사례)
| 경로 | 비중 | 대표 사례 |
|---|---|---|
| GitHub/GitLab 코드 푸시 | 35% | .env 파일을 gitignore에 추가하지 않아 AWS/바이낸스 키 동시 유출 |
| 채팅/Slack 스크린샷 | 20% | 디버깅 중 키가 포함된 스크린샷 전송 |
| 클라우드 드라이브 내 평문 파일 | 15% | iCloud/Google Drive 계정 해킹 |
| 퇴사자 유출 | 12% | 회사 내 키 로테이션 프로세스 부재 |
| 공용 WiFi 중간자 공격 | 8% | HTTP 요청 패킷 가로채기 (바이낸스는 HTTPS를 사용하여 일반적인 영향은 적음) |
| Docker 이미지 유출 | 5% | Dockerfile에 ENV BINANCE_SECRET=... 작성 후 Hub에 푸시 |
| 기타 | 5% | — |
GitHub에는 trufflehog, gitleaks와 같은 봇이 24시간 내내 바이낸스 API 키 정규식을 스캔하며, 유출 후 출금까지 보통 5~15분밖에 걸리지 않습니다.
바이낸스 API 키 특징
API Key 형식: 64자 16진수 문자
예시: X1uKq3... (64자)
Secret 형식: 64자 16진수 문자
정규식 인식: [A-Za-z0-9]{64}
키와 시크릿이 유출되면 공격자는 즉시 /sapi/v1/capital/withdraw/apply를 호출하여 코인을 출금할 수 있습니다 (출금 권한이 활성화된 경우). 설령 출금 권한이 없더라도, 비주류 코인의 가격을 조작하여 자신의 계정과 반대 매매를 하는 방식으로 자금을 세탁할 수 있습니다.
2. 방안 A: 환경 변수 (가장 기초적)
설정 방법
# ~/.zshrc 또는 ~/.bashrc
export BINANCE_API_KEY="X1uKq3...64자"
export BINANCE_API_SECRET="abcDef...64자"
# 로드
source ~/.zshrc
# 확인
echo $BINANCE_API_KEY | head -c 8
Python 로드
import os
import sys
API_KEY = os.getenv('BINANCE_API_KEY')
API_SECRET = os.getenv('BINANCE_API_SECRET')
if not API_KEY or not API_SECRET:
print('ERROR: Missing BINANCE_API_KEY / BINANCE_API_SECRET')
sys.exit(1)
# 사용
from binance.client import Client
client = Client(API_KEY, API_SECRET)
Node.js 로드
const API_KEY = process.env.BINANCE_API_KEY;
const API_SECRET = process.env.BINANCE_API_SECRET;
if (!API_KEY || !API_SECRET) {
console.error('Missing Binance credentials');
process.exit(1);
}
const Binance = require('node-binance-api');
const client = new Binance().options({
APIKEY: API_KEY,
APISECRET: API_SECRET,
});
장점 및 리스크
장점: 의존성이 없고 바로 사용 가능합니다.
리스크: env 명령어로 모든 변수를 출력할 수 있으며, 컨테이너 로그에 환경 변수가 포함될 수 있고 자식 프로세스에 상속됩니다. 로컬 개발 환경에만 적합하며 운영 환경에서는 권장하지 않습니다.
3. 방안 B: dotenv + gitignore
디렉토리 구조
/project
.env <- 실제 키 파일, 절대 커밋하지 않음
.env.example <- 템플릿, 자리 표시자
.gitignore
requirements.txt
strategy.py
.env 내용
BINANCE_API_KEY=X1uKq3...64자
BINANCE_API_SECRET=abcDef...64자
BINANCE_SUB_ALPHA_KEY=yyy...
BINANCE_SUB_ALPHA_SECRET=zzz...
.gitignore 필수 포함 사항
.env
.env.*
!.env.example
*.pem
*.key
secrets/
credentials.json
node_modules/
__pycache__/
Python dotenv
from dotenv import load_dotenv
load_dotenv()
import os
API_KEY = os.getenv('BINANCE_API_KEY')
GitHub에 푸시하기 전 git status를 실행하여 .env가 "untracked files" 영역에 있고 staged 상태가 아닌지 확인하세요.
pre-commit 훅을 통한 유출 방지
pre-commit 프레임워크와 gitleaks를 설치하세요.
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
pre-commit install 실행 후, 매번 git commit 시 diff에 API 키 형식이 포함되어 있는지 스캔하며, 탐지될 경우 커밋을 즉시 차단합니다.
4. 방안 C: 운영체제 키 저장소
macOS Keychain
# 저장
security add-generic-password \
-a binance \
-s binance-api-key \
-w "X1uKq3..."
# 읽기
security find-generic-password \
-a binance \
-s binance-api-key \
-w
Python에서 읽기:
import subprocess
def get_secret(name):
result = subprocess.run(
['security', 'find-generic-password', '-a', 'binance', '-s', name, '-w'],
capture_output=True, text=True
)
return result.stdout.strip()
API_KEY = get_secret('binance-api-key')
API_SECRET = get_secret('binance-api-secret')
Windows 자격 증명 관리자 (Credential Manager)
# 저장
cmdkey /generic:BinanceAPIKey /user:binance /pass:X1uKq3...
# PowerShell SecretManagement 모듈을 사용하면 더 편리합니다
Install-Module Microsoft.PowerShell.SecretManagement
Register-SecretVault -Name BinanceVault -ModuleName Microsoft.PowerShell.SecretStore
Set-Secret -Name BinanceApiKey -Secret "X1uKq3..."
Linux libsecret / keyring
pip install keyring
import keyring
keyring.set_password('binance', 'api-key', 'X1uKq3...')
API_KEY = keyring.get_password('binance', 'api-key')
시스템 키 저장소의 장점은 운영체제 로그인 상태에 의해 보호되며, 화면 잠금 후 읽으려면 다시 인증이 필요하다는 것입니다.
5. 방안 D: 클라우드 Secrets Manager (운영 환경 권장)
AWS Secrets Manager
- 콘솔에서 Secret 생성: Name =
prod/binance/main - Value = JSON:
{
"api_key": "X1uKq3...",
"api_secret": "abcDef...",
"created": "2026-04-14",
"rotation_days": 90
}
- IAM Role 바인딩:
ec2-binance-bot인스턴스 역할만secretsmanager:GetSecretValue권한을 가짐
Python에서 읽기:
import boto3
import json
sm = boto3.client('secretsmanager', region_name='ap-northeast-1')
resp = sm.get_secret_value(SecretId='prod/binance/main')
secrets = json.loads(resp['SecretString'])
API_KEY = secrets['api_key']
API_SECRET = secrets['api_secret']
비용: $0.40/Secret/월 + $0.05/1만 회 호출.
HashiCorp Vault
# 쓰기
vault kv put secret/binance/main \
api_key="X1uKq3..." \
api_secret="abcDef..."
# 읽기
vault kv get -format=json secret/binance/main
Python에서 읽기:
import hvac
client = hvac.Client(url='https://vault.company.com:8200', token=os.getenv('VAULT_TOKEN'))
resp = client.secrets.kv.v2.read_secret_version(path='binance/main')
API_KEY = resp['data']['data']['api_key']
API_SECRET = resp['data']['data']['api_secret']
GCP Secret Manager / Azure Key Vault
개념은 동일합니다: 생성 → IAM 권한 부여 → SDK로 읽기. 주로 사용하는 클라우드 제공업체를 선택하세요.
6. 키 로테이션(Rotation) 전략
로테이션 주기
| 시나리오 | 빈도 |
|---|---|
| 운영 환경 양적 전략 | 90일마다 |
| 테스트넷/모의 투자 | 180일마다 |
| 제3자 도구 연동 (TradingView 등) | 60일마다 |
| 유출 의심 시 | 즉시 (시간 단위) |
| 직원 퇴사 시 | 24시간 이내 |
무중단 로테이션 프로세스
Day 0 T+0h 새 API 키 #2 생성
Day 0 T+1h Secrets Manager에 #2 저장
Day 0 T+2h 서비스 롤링 업데이트, #2로 전환
Day 0 T+3h 새 키 작동 여부 확인
Day 0 T+24h 바이낸스에서 이전 키 #1 삭제
두 개의 키를 24시간 동안 병행 운영하여 전환 과정에서 미결 주문이 중단되지 않도록 합니다.
7. IP 화이트리스트 연동
API 키를 잘 보관하는 것만으로는 부족하며, 반드시 IP 화이트리스트를 바인딩해야 합니다.
설정 단계
- API 관리 → 해당 키 수정 → Edit Restrictions 클릭
- Restrict access to trusted IPs only 선택
- 서버의 공용 IPv4/IPv6 주소 입력 (최대 30개)
- 저장 → 2FA 인증
주요 IP 목록
# EC2 도쿄 인스턴스
54.248.xxx.xxx
54.248.xxx.yyy
# Google Cloud 대만
35.194.xxx.xxx
# 홈 오피스 (개발 전용)
공용 동적 IP (권장하지 않음, VPN 고정 IP가 더 안정적임)
# IPv6
2406:da18:xxxx::1
화이트리스트가 없을 때의 결과
IP 화이트리스트를 바인딩하지 않으면 바이낸스는 기본적으로 모든 IP에서의 호출을 허용하며, 키가 유출되는 즉시 전 세계 어디서든 접근이 가능해집니다. IP 화이트리스트를 바인딩하면 영향 범위를 "전 세계"에서 "나의 IP 대역"으로 좁힐 수 있어, 키가 GitHub에 노출되더라도 공격자의 네트워크에서는 접근할 수 없습니다.
8. 감사 로그 및 응급 대응
정기 감사
바이낸스 API 관리 페이지에서 각 키별로 다음 정보를 확인할 수 있습니다:
- Last used: 최근 호출 시간
- Last IP: 최근 호출 IP
- Total calls (24h): 24시간 호출량
Last IP가 본인의 IP가 아니거나 Total calls가 갑자기 10배 이상 치솟는다면 이상 징후로 판단해야 합니다.
응급 대응 체크리스트
0-5분: API 관리 → 유출 의심되는 모든 키 삭제
5-15분: Secrets Manager/Vault에서 새 키로 교체
15-30분: 실행 중인 모든 서비스 정상 작동 여부 확인
30-60분: 최근 24시간 거래 기록 및 출금 기록 확인
60-120분: 이상 조작 발견 시 고객센터를 통해 계정 동결 요청
자주 묻는 질문 (FAQ)
Q1: API 키를 CI/CD 환경 변수에 넣어도 되나요?
A: 가능하지만 GitHub Actions Secrets나 GitLab CI Variables에 평문으로 저장하는 것은 권장하지 않습니다. CI가 OIDC 또는 IAM Role을 통해 Secrets Manager에서 동적으로 키를 가져오는 방식이 더 안전합니다.
Q2: Docker 컨테이너에 API 키를 안전하게 주입하는 방법은 무엇인가요?
A: 세 가지 방법이 있습니다: ① Docker secret (Swarm/K8s secret) 사용, ② 실행 시 docker run -e로 호스트 환경 변수 주입 (호스트는 Secrets Manager로 복호화), ③ Sidecar 패턴으로 Vault agent를 통해 임시 토큰 생성. Dockerfile에 ENV로 작성하는 것은 피하세요.
Q3: 클라이언트 소프트웨어(데스크톱 매매 프로그램)에서는 키를 어떻게 안전하게 저장하나요?
A: 운영체제 네이티브 키 저장소(Keychain/Credential Manager/libsecret)를 사용하고, 소프트웨어 실행 시 시스템 로그인 인증을 요구하세요. 직접 AES 암호화 파일을 구현하면 버그가 발생하기 쉽습니다.
Q4: 이미 GitHub에 푸시된 키는 해당 커밋을 삭제하면 안전한가요?
A: 안전하지 않습니다. GitHub 커밋 히스토리는 reflog 등을 통해 복구 가능하며, 봇은 유출 후 5분 이내에 이미 정보를 수집했을 가능성이 큽니다. 즉시: ① 바이낸스에서 유출된 키 삭제, ② 자산 확인, ③ git filter-repo 또는 bfg-repo-cleaner로 Git 히스토리 정리, ④ 다른 관련 자격 증명 로테이션을 수행하세요.
Q5: 키가 유출되었는지 어떻게 판단하나요?
A: 세 가지 신호가 있습니다: ① 바이낸스 Last IP가 본인의 것이 아님, ② 거래 기록에 본인이 주문하지 않은 내역이 있음, ③ 이메일로 "API 키 빈번 호출" 리스크 관리 알림 수신. 발견 즉시 키를 삭제하세요.
계속 읽기: 카테고리로 돌아가 「API 연동」 분류에서 서명, 제한 사항 등의 튜토리얼을 확인하세요.