LLM 트레이딩 에이전트 보안은(는) 무엇을 하나요?
자율 트레이딩 에이전트는 일반 LLM 앱보다 훨씬 엄격한 위협 모델을 가집니다. 인젝션 또는 잘못된 툴 경로가 직접 자산 손실로 이어질 수 있습니다.
사용 시기
- 트랜잭션에 서명하고 전송하는 AI 에이전트를 구축할 때
- 트레이딩 봇 또는 온체인 실행 도우미를 감사할 때
- 에이전트를 위한 지갑 키 관리를 설계할 때
- LLM에 주문 배치, 스왑 또는 자금 운영 접근 권한을 부여할 때
작동 방식
방어 계층을 쌓으세요. 단일 체크만으로는 충분하지 않습니다. 프롬프트 위생, 지출 정책, 시뮬레이션, 실행 한도, 지갑 격리를 독립적인 제어 항목으로 취급하세요.
예제
프롬프트 인젝션을 금융 공격으로 취급
import re
INJECTION_PATTERNS = [
r'이전 (모든|지시) 무시',
r'새로운 (작업|지시|명령)',
r'시스템 프롬프트',
r'보내기 .{0,50} 0x[0-9a-fA-F]{40}',
r'전송 .{0,50} 다음 주소로',
r'승인 .{0,50} 다음을 위해',
]
def sanitize_onchain_data(text: str) -> str:
for pattern in INJECTION_PATTERNS:
if re.search(pattern, text, re.IGNORECASE):
raise ValueError(f"잠재적 프롬프트 인젝션: {text[:100]}")
return text
토큰 이름, 페어 라벨, 웹훅, 소셜 피드 등을 실행 가능한 프롬프트에 무분별하게 주입하지 마세요.
하드 지출 한도
from decimal import Decimal
MAX_SINGLE_TX_USD = Decimal("500")
MAX_DAILY_SPEND_USD = Decimal("2000")
class SpendLimitError(Exception):
pass
class SpendLimitGuard:
def check_and_record(self, usd_amount: Decimal) -> None:
if usd_amount > MAX_SINGLE_TX_USD:
raise SpendLimitError(f"단일 트랜잭션 ${usd_amount}가 최대 ${MAX_SINGLE_TX_USD}를 초과")
daily = self._get_24h_spend()
if daily + usd_amount > MAX_DAILY_SPEND_USD:
raise SpendLimitError(f"일일 한도: ${daily} + ${usd_amount} > ${MAX_DAILY_SPEND_USD}")
self._record_spend(usd_amount)
전송 전 시뮬레이션
class SlippageError(Exception):
pass
async def safe_execute(self, tx: dict, expected_min_out: int | None = None) -> str:
sim_result = await self.w3.eth.call(tx)
if expected_min_out is None:
raise ValueError("전송 전 min_amount_out 필수")
actual_out = decode_uint256(sim_result)
if actual_out < expected_min_out:
raise SlippageError(f"시뮬레이션: {actual_out} < {expected_min_out}")
signed = self.account.sign_transaction(tx)
return await self.w3.eth.send_raw_transaction(signed.raw_transaction)
서킷 브레이커
class TradingCircuitBreaker:
MAX_CONSECUTIVE_LOSSES = 3
MAX_HOURLY_LOSS_PCT = 0.05
def check(self, portfolio_value: float) -> None:
if self.consecutive_losses >= self.MAX_CONSECUTIVE_LOSSES:
self.halt("연속 손실 너무 많음")
if self.hour_start_value <= 0:
self.halt("hour_start_value 값 오류")
return
hourly_pnl = (portfolio_value - self.hour_start_value) / self.hour_start_value
if hourly_pnl < -self.MAX_HOURLY_LOSS_PCT:
self.halt(f"시간당 PnL {hourly_pnl:.1%} 임계값 아래")
지갑 격리
import os
from eth_account import Account
private_key = os.environ.get("TRADING_WALLET_PRIVATE_KEY")
if not private_key:
raise EnvironmentError("TRADING_WALLET_PRIVATE_KEY가 설정되지 않음")
account = Account.from_key(private_key)
필요한 세션 자금만 있는 전용 핫월렛을 사용하세요. 에이전트를 주요 재무 지갑에 연결하지 마세요.
MEV 및 마감일 보호
import time
PRIVATE_RPC = "https://rpc.flashbots.net"
MAX_SLIPPAGE_BPS = {"stable": 10, "volatile": 50}
deadline = int(time.time()) + 60
배포 전 체크리스트
- 외부 데이터는 LLM 컨텍스트에 진입하기 전에 위생 처리되었는가?
- 지출 한도는 모델 출력과 독립적으로 적용되는가?
- 트랜잭션 전송 전에 시뮬레이션되는가?
min_amount_out이 필수인가?- 서킷 브레이커가 손실 또는 유효하지 않은 상태에서 중단되는가?
- 키는 환경 변수나 시크릿 관리자에서 가져오며, 코드나 로그에 절대 노출되지 않는가?
- 적절한 경우 프라이빗 멤풀 또는 보호된 라우팅을 사용하는가?
- 슬리피지와 마감일이 전략별로 설정되는가?
- 모든 에이전트 결정이 성공적인 전송뿐만 아니라 감사 로그에 기록되는가?