feat: Phase 3 보안 강화 - API 키 AES-256 암호화

- server/encryption.py: AES-256 암호화/복호화 함수 추가
- test_admin.py: API 키 암호화 저장 및 조회 로직 구현
- static/admin.js: 암호화 상태 표시 UI 추가
- static/admin.css: 암호화 배지 스타일 추가

API 키가 이제 AES-256으로 암호화되어 저장됩니다.
This commit is contained in:
Hyungi Ahn
2025-08-19 15:29:53 +09:00
parent 1e098999c1
commit 841178ed7e
4 changed files with 240 additions and 20 deletions

View File

@@ -20,6 +20,18 @@ from fastapi.templating import Jinja2Templates
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import uvicorn
# Import encryption module
try:
from server.encryption import encrypt_api_key, decrypt_api_key, is_encrypted
ENCRYPTION_AVAILABLE = True
except ImportError:
print("⚠️ Encryption module not available, using plain text storage")
ENCRYPTION_AVAILABLE = False
def encrypt_api_key(key): return key
def decrypt_api_key(key): return key
def is_encrypted(key): return False
# FastAPI 앱 초기화
app = FastAPI(title="AI Server Admin Dashboard (Test Mode)")
@@ -51,21 +63,44 @@ TEST_USERS = {
}
}
# 임시 데이터 저장소
api_keys_store = {
"test-key-1": {
"name": "Test Key 1",
"key": "test-api-key-abcd1234",
"created_at": datetime.now().isoformat(),
"usage_count": 42,
},
"test-key-2": {
"name": "Development Key",
"key": "dev-api-key-efgh5678",
"created_at": datetime.now().isoformat(),
"usage_count": 128,
# 임시 데이터 저장소 (암호화된 API 키)
def initialize_api_keys():
"""Initialize API keys with encryption"""
keys = {
"test-key-1": {
"name": "Test Key 1",
"key": "test-api-key-abcd1234",
"created_at": datetime.now().isoformat(),
"usage_count": 42,
},
"test-key-2": {
"name": "Development Key",
"key": "dev-api-key-efgh5678",
"created_at": datetime.now().isoformat(),
"usage_count": 128,
}
}
}
# Encrypt API keys if encryption is available
if ENCRYPTION_AVAILABLE:
for key_id, key_data in keys.items():
if not is_encrypted(key_data["key"]):
try:
key_data["key"] = encrypt_api_key(key_data["key"])
key_data["encrypted"] = True
print(f"🔒 Encrypted API key: {key_data['name']}")
except Exception as e:
print(f"❌ Failed to encrypt key {key_data['name']}: {e}")
key_data["encrypted"] = False
else:
key_data["encrypted"] = True
else:
for key_data in keys.values():
key_data["encrypted"] = False
return keys
api_keys_store = initialize_api_keys()
# 테스트용 모델 데이터
test_models = [
@@ -307,15 +342,25 @@ async def admin_test_model(request: dict, api_key: str = Depends(require_api_key
@app.get("/admin/api-keys")
async def admin_get_api_keys(api_key: str = Depends(require_api_key)):
"""API 키 목록 조회"""
"""API 키 목록 조회 (복호화된 키 반환)"""
keys = []
for key_id, key_data in api_keys_store.items():
# Decrypt key for display
display_key = key_data.get("key", "")
if ENCRYPTION_AVAILABLE and key_data.get("encrypted", False):
try:
display_key = decrypt_api_key(display_key)
except Exception as e:
print(f"❌ Failed to decrypt key {key_data.get('name')}: {e}")
display_key = "DECRYPTION_FAILED"
keys.append({
"id": key_id,
"name": key_data.get("name", "Unnamed"),
"key": key_data.get("key", ""),
"key": display_key,
"created_at": key_data.get("created_at", datetime.now().isoformat()),
"usage_count": key_data.get("usage_count", 0),
"encrypted": key_data.get("encrypted", False),
})
return {"api_keys": keys}
@@ -323,19 +368,37 @@ async def admin_get_api_keys(api_key: str = Depends(require_api_key)):
@app.post("/admin/api-keys")
async def admin_create_api_key(request: dict, api_key: str = Depends(require_api_key)):
"""새 API 키 생성"""
"""새 API 키 생성 (암호화 저장)"""
name = request.get("name", "Unnamed Key")
new_key = secrets.token_urlsafe(32)
key_id = str(uuid.uuid4())
# Encrypt the key before storing
stored_key = new_key
encrypted = False
if ENCRYPTION_AVAILABLE:
try:
stored_key = encrypt_api_key(new_key)
encrypted = True
print(f"🔒 Created encrypted API key: {name}")
except Exception as e:
print(f"❌ Failed to encrypt new key {name}: {e}")
encrypted = False
api_keys_store[key_id] = {
"name": name,
"key": new_key,
"key": stored_key,
"created_at": datetime.now().isoformat(),
"usage_count": 0,
"encrypted": encrypted,
}
return {"api_key": new_key, "key_id": key_id}
return {
"api_key": new_key, # Return plain key to user (only time they'll see it)
"key_id": key_id,
"encrypted": encrypted
}
@app.delete("/admin/api-keys/{key_id}")