보안

보안 (Security)

vLLM을 프로덕션에 노출한다면, "기본값이 안전한가?"라는 질문부터 따져야 해요. vLLM은 성능을 위해 많은 통신을 기본적으로 암호화하지 않고, 일부 엔드포인트는 인증 없이 열려 있습니다. 이 문서는 vLLM 배포를 보호하기 위해 알아야 할 보안 고려사항들을 정리해요.

출처: vLLM 공식 문서 — security

노드 간 통신 (Inter-Node Communication)

멀티 노드 vLLM 배포에서 노드 간 모든 통신은 기본적으로 안전하지 않아요. 노드를 격리된 네트워크에 두어 보호해야 합니다. 여기에는 다음이 포함돼요.

  • PyTorch Distributed 통신
  • KV 캐시 전송 통신
  • 텐서·파이프라인·데이터 병렬 통신

노드 간 통신 구성 옵션

다음 옵션들이 vLLM의 노드 간 통신을 제어해요.

  1. 환경 변수:
    • VLLM_HOST_IP: vLLM 프로세스가 통신할 IP 주소 설정
  2. KV 캐시 전송 구성:
    • --kv-ip: KV 캐시 전송 통신용 IP 주소 (기본값: 127.0.0.1)
    • --kv-port: KV 캐시 전송 통신용 포트 (기본값: 14579)
  3. 데이터 병렬 구성:
    • data_parallel_master_ip: 데이터 병렬 마스터의 IP (기본값: 127.0.0.1)
    • data_parallel_master_port: 데이터 병렬 마스터의 포트 (기본값: 29500)

PyTorch Distributed 참고 사항

vLLM은 일부 노드 간 통신에 PyTorch의 분산 기능을 사용해요. PyTorch 보안 가이드의 핵심 요점은 다음과 같습니다.

  • PyTorch Distributed 기능은 내부 통신 전용으로 설계됨
  • 신뢰할 수 없는 환경이나 네트워크에서 쓰도록 만들어지지 않음
  • 성능을 위해 인증 프로토콜이 포함되지 않음
  • 메시지가 암호화되지 않고 전송됨
  • 연결이 어디서든 검사 없이 수락됨

보안 권장사항 (Security Recommendations)

  1. 네트워크 격리: vLLM 노드를 전용 격리 네트워크에 배포하고, 네트워크 분할로 무단 접근을 막으며, 적절한 방화벽 규칙을 구현하세요.
  2. 구성 모범 사례: 항상 VLLM_HOST_IP를 기본값 대신 특정 IP로 설정하고, 노드 간에 필요한 포트만 방화벽에서 허용하세요.
  3. 접근 제어: 배포 환경에 대한 물리적·네트워크 접근을 제한하고, 관리 인터페이스에 적절한 인증·권한 부여를 구현하며, 모든 시스템 구성 요소에 최소 권한 원칙을 따르세요.
  4. 미디어 URL 도메인 제한: --allowed-media-domains를 설정해 SSRF(Server-Side Request Forgery) 공격을 방지하세요. (예: --allowed-media-domains upload.wikimedia.org github.com www.bogotobogo.com) 이 보호는 온라인 서빙 API(멀티모달 입력)와 배치 러너(vllm run-batch) 양쪽에 적용돼요. 배치 전사/번역 요청의 file_url 값도 같은 allowlist로 검증됩니다.
  5. 미디어 다운로드·디코드 크기 제한: 원격 미디어 응답과 압축 미디어 파일은 수 기가바이트 메모리로 확장될 수 있어요. vLLM은 OOM(denial of service)을 막기 위해 다운로드·디코드 크기 제한을 적용합니다.
환경 변수 기본값 설명
VLLM_MAX_MEDIA_DOWNLOAD_SIZE_MB 256 단일 원격 미디어 응답의 최대 크기(MB). 과대 응답은 전체 본문이 메모리에 실체화되기 전에 스트리밍 중 거부됨
VLLM_MAX_IMAGE_PIXELS 178956970 (~1억 7900만 픽셀) 디코드된 이미지의 최대 픽셀 크기. 래스터 메모리 할당 전에 초과 이미지가 거부됨. 기본값은 PIL의 내장 2x 압축 폭탄 임계값(~RGB 680MB)과 일치
VLLM_MAX_AUDIO_CLIP_FILESIZE_MB 25 단일 오디오 파일의 최대 압축 파일 크기(MB). 디코드 시작 전에 모든 오디오 입력(멀티모달 채팅 URL, speech-to-text 업로드, data: URL, 로컬 파일 경로)에 적용
VLLM_MAX_AUDIO_DECODE_DURATION_S 600 최대 디코드된 오디오 지속 시간(초). 압축 오디오가 수 기가바이트 float32 PCM으로 확장되는 것을 방지
VLLM_MAX_AUDIO_DECODE_BYTES 268435456 (256 MiB) 오디오 디코딩이 할당할 수 있는 최대 float32 PCM 바이트. 부풀려진 헤더 샘플레이트가 duration 가드를 우회하고 실제 프레임 수가 multi-GiB 할당을 유발하는 샘플레이트 위조를 방지
VLLM_MAX_EMBED_DECODE_BYTES 2147483648 (2 GiB) 클라이언트가 제공한 임베딩 페이로드(prompt_embeds, image_embeds, audio_embeds, video_embeds)가 밀집화(densify) 후 할당할 수 있는 최대 바이트. 희소 텐서는 자체 선언 모양을 가지므로 수백 바이트 페이로드가 수백 GiB로 확장될 수 있음. to_dense() 전에 검사되어 메모리가 절대 할당되지 않음. 0으로 설정해 비활성화

이 중 하나라도 0으로 설정하면 해당 제한이 비활성화돼요. 신뢰할 수 없는 사용자에게 노출되는 배포에서는 권장되지 않습니다.

방화벽과 노출된 vLLM 시스템 보호

vLLM은 안전하지 않은 네트워크 서비스를 사설 네트워크로 격리할 수 있게 설계됐지만, 의존성이나 기반 프레임워크 같은 구성 요소가 vLLM의 직접 통제 밖에서 모든 네트워크 인터페이스에서 수신 대기하는 안전하지 않은 서비스를 열 수도 있어요.

가장 큰 우려는 torch.distributed예요. vLLM이 TCP 초기화를 사용하면 PyTorch가 기본적으로 모든 네트워크 인터페이스에서 수신 대기하는 TCPStore를 만들어요. 즉 추가 보호가 없으면 이 서비스들이 어떤 네트워크 인터페이스로든 당신 머신에 닿을 수 있는 호스트에 접근 가능할 수 있습니다. PyTorch 관점에서 torch.distributed의 어떤 사용도 기본적으로 안전하지 않은 것으로 간주해야 해요.

방화벽 구성 지침

vLLM 시스템을 보호하는 최선의 방법은 최소한의 네트워크 표면만 노출하도록 방화벽을 신중히 구성하는 거예요. 대부분의 경우:

  • API 서버가 수신 대기하는 TCP 포트를 제외한 모든 인바운드 연결을 차단하세요.
  • 내부 통신용 포트(예: torch.distributed, KV 캐시 전송)는 신뢰할 수 있는 호스트나 네트워크에서만 접근 가능하게 하세요.
  • 이런 내부 포트를 공개 인터넷이나 신뢰할 수 없는 네트워크에 절대 노출하지 마세요.

API 키 인증의 한계 (API Key Authentication Limitations)

--api-key 플래그(또는 VLLM_API_KEY 환경 변수)는 vLLM HTTP 서버에 인증을 제공하지만, /v1 경로 접두사 아래의 OpenAI 호환 API 엔드포인트와 그와 유사한 /v2, /inference 경로 접두사에만 적용돼요. 아주 많은 민감한 엔드포인트가 같은 HTTP 서버에서 인증 없이 노출됩니다.

중요: vLLM에 대한 접근을 보호하기 위해 --api-key에만 의존하지 마세요. 프로덕션 배포에는 추가 보안 조치가 필요합니다.

보호되는 엔드포인트 (API 키 필요)

--api-key를 구성하면 다음 /v1 엔드포인트가 Bearer 토큰 인증을 요구해요:

  • /v1/models — 사용 가능한 모델 목록
  • /v1/chat/completions — 채팅 완성
  • /v1/chat/completions/batch — 배치 채팅 완성
  • /v1/completions — 텍스트 완성
  • /v1/embeddings — 임베딩 생성
  • /v1/audio/transcriptions — 오디오 전사
  • /v1/audio/translations — 오디오 번역
  • /v1/messages — Anthropic 호환 messages API
  • /v1/messages/count_tokens — Anthropic 메시지 토큰 수 세기
  • /v1/responses — 응답 생성
  • /v1/responses/{response_id} — 응답 조회
  • /v1/score — 스코어링 API
  • /v1/rerank — 리랭킹 API

이 외에도 /inference/v1/generate, Cohere API 경로, LoRA 어댑터 로드/언로드 엔드포인트 등이 있습니다.

보호되지 않는 엔드포인트 (API 키 불필요)

--api-key를 구성해도 인증을 요구하지 않는 엔드포인트들이 있어요.

추론 엔드포인트:

  • /invocations — SageMaker 호환 엔드포인트 (/v1 엔드포인트와 같은 추론 함수로 라우팅)
  • /generative_scoring — 생성 스코어링 API
  • /pooling — 풀링 API
  • /classify — 분류 API
  • /rerank — 리랭킹 API

운영 제어 엔드포인트 (모델이 "generate" 작업을 지원할 때): /pause, /resume, /is_paused, /abort_requests, /scale_elastic_ep, /init_weight_transfer_engine, /update_weights, /get_world_size

유틸리티 엔드포인트: /tokenize, /detokenize, /health, /ping, /version, /load

개발 엔드포인트 (VLLM_SERVER_DEV_MODE=1일 때만): /server_info, /reset_prefix_cache, /sleep, /wake_up, /collective_rpc 등. 개발·디버깅용이며 프로덕션에서 절대 활성화하면 안 돼요.

참고: /invocations 엔드포인트가 특히 우려되는데, 보호되는 /v1 엔드포인트와 동일한 추론 기능을 인증 없이 제공하기 때문이에요.

보안 영향

vLLM HTTP 서버에 닿을 수 있는 공격자는:

  • /invocations, /generative_scoring, /pooling 같은 비-/v1 엔드포인트를 사용해 자격 증명 없이 임의 추론 실행 가능
  • 토큰 없이 /pause, /scale_elastic_ep, /abort_requests를 호출해 서비스 거부(denial of service) 유발
  • 운영 제어로 서버 상태 조작 (예: 생성 일시정지, /update_weights로 모델 가중치 갱신)

권장 보안 관행

  1. 노출 엔드포인트 최소화:
    • CRITICAL: 프로덕션 환경에서 절대 VLLM_SERVER_DEV_MODE=1을 설정하지 마세요. 개발 엔드포인트는 /collective_rpc를 통한 임의 RPC 실행, 캐시 조작, 상세 서버 구성 노출 같은 매우 위험한 기능을 드러냅니다. 프로파일러 엔드포인트도 프로덕션에서 절대 활성화하지 마세요.
    • --enable-tokenizer-info-endpoint는 신중히 사용하세요. /tokenizer_info 엔드포인트는 채팅 템플릿과 토크나이저 설정을 드러내는데, 여기에는 민감한 구현 세부사항이나 프롬프트 엔지니어링 전략이 포함될 수 있어요.
  2. 리버스 프록시 뒤에 배포: 가장 효과적인 접근은 vLLM을 리버스 프록시(nginx, Envoy, Kubernetes Gateway 등) 뒤에 배포해, 엔드 사용자에게 노출하려는 엔드포인트만 명시적으로 allowlist하고, 인증되지 않은 추론·운영 제어 엔드포인트를 포함한 다른 모든 것을 차단하는 거예요. 프록시 레이어에서 추가 인증, 요율 제한, 로깅을 구현하세요.

요청 파라미터 리소스 제한

일부 API 요청 파라미터는 리소스 소비에 큰 영향을 주고 서버 리소스를 고갈시키는 데 악용될 수 있어요. /v1/completions/v1/chat/completions 엔드포인트의 n 파라미터는 요청당 생성되는 독립 출력 시퀀스 수를 제어해요. 매우 큰 값은 n에 비례해 메모리·CPU·GPU 시간을 할당하게 해, 호스트에서 OOM 조건을 일으키고 다른 요청 처리를 막을 수 있습니다.

vLLM은 VLLM_MAX_N_SEQUENCES 환경 변수(기본값: 16384)로 n 파라미터의 구성 가능한 상한을 강제해요. 이 한도를 초과하는 요청은 엔진에 도달하기 전에 거부됩니다.

권장사항:

  • 공개-facing 배포: 워크로드에 적합한 값(예: 64 또는 128)으로 VLLM_MAX_N_SEQUENCES를 설정해 단일 요청의 피해 반경을 제한하세요.
  • 리버스 프록시 레이어: 요청 본문 검증과 요율 제한을 프록시에서 시행해 악의적인 페이로드를 더 제한하세요.
  • 모니터링: 요청별 리소스 소비를 모니터링해 악용을 나타낼 수 있는 비정상 패턴을 감지하세요.

도구 서버와 MCP 보안

vLLM은 --tool-server 인자를 통해 외부 도구 서버 연결을 지원해요. 이는 모델이 Responses API(/v1/responses)를 통해 도구를 호출할 수 있게 합니다. 도구 서버 지원은 모든 모델에서 동작하고, 특정 모델 아키텍처로 제한되지 않아요.

중요: 기본적으로 활성화된 도구 서버는 없어요. 구성으로 명시적으로 옵트인해야 합니다.

내장 데모 도구 (GPT-OSS)

--tool-server demo를 넘기면 도구 호출을 지원하는 모든 모델에서 동작하는 내장 데모 도구가 활성화돼요. 도구 구현은 vLLM의 일부가 아니라 별도 설치된 gpt-oss 패키지에서 제공됩니다. vLLM은 gpt-oss에 위임하는 얇은 래퍼를 제공해요.

  • 코드 인터프리터 (python): Docker를 통해 Python 실행 (gpt_oss.tools.python_docker)
  • 웹 브라우저 (browser): Exa API로 검색, EXA_API_KEY 필요 (gpt_oss.tools.simple_browser)

코드 인터프리터 (Python 도구) 보안 위험

코드 인터프리터는 모델이 생성한 코드를 Docker 컨테이너 안에서 실행해요. 하지만 컨테이너는 기본적으로 네트워크 격리가 구성되어 있지 않습니다. 호스트의 Docker 네트워킹 구성(기본 bridge 네트워크나 --network=host)을 상속하므로:

  • 컨테이너가 호스트 네트워크와 LAN에 접근할 수 있음
  • 컨테이너에서 닿을 수 있는 내부 서비스가 SSRF로 악용될 수 있음
  • 클라우드 메타데이터 서비스(예: 169.254.169.254)에 접근 가능할 수 있음
  • 컨테이너에서 닿을 수 있는 취약한 내부 서비스(예: torch.distributed 엔드포인트)가 공격되는 데 사용될 수 있음

실행되는 코드가 모델이 생성한 것이고 적대적 입력(프롬프트 인젝션)의 영향을 받을 수 있기 때문에 특히 우려됩니다.

내장 도구 가용성 제어

내장 데모 도구는 두 가지 설정으로 제어돼요.

  • --tool-server demo: 내장 데모 도구(browser와 Python 코드 인터프리터) 활성화.
  • VLLM_GPT_OSS_SYSTEM_TOOL_MCP_LABELS: Responses API에서 MCP 도구 타입으로 내장 도구가 요청될 때, 어떤 도구 라벨을 허용할지 제어하는 쉼표 구분 allowlist. 유효한 값은 container(컨테이너 도구), code_interpreter(Python 코드 실행 도구), web_search_preview(웹 검색/브라우저 도구). 설정하지 않거나 비어 있으면 MCP 도구 타입으로 요청된 내장 도구가 활성화되지 않아요.

Python 코드 인터프리터를 비활성화하려면 VLLM_GPT_OSS_SYSTEM_TOOL_MCP_LABELS에서 code_interpreter를 생략하세요. GPT-OSS Python 도구는 참조 구현이므로, 프로덕션 배포에서는 더 엄격한 격리 보장을 가진 커스텀 코드 실행 샌드박스를 구현하는 것을 고려하세요.

동적 LoRA 로딩

vLLM은 /v1/load_lora_adapter/v1/unload_lora_adapter API 엔드포인트를 통해 런타임에 LoRA 어댑터를 동적으로 로드·언로드할 수 있어요. 이 기능은 기본적으로 활성화되지 않고 --enable-loraVLLM_ALLOW_RUNTIME_LORA_UPDATING=True 환경 변수 둘 다 필요합니다.

경고: 동적 LoRA 로딩은 안전한 작업이 아니며 신뢰할 수 없는 클라이언트에 노출되는 배포에서 활성화하면 안 돼요. 반드시 활성화해야 한다면 리버스 프록시나 네트워크 수준 접근 제어로 /v1/load_lora_adapter/v1/unload_lora_adapter 엔드포인트 접근을 신뢰할 수 있는 관리자로만 제한하세요. 엔드 사용자에게 노출하지 마세요.

gRPC 인터페이스

vLLM은 --grpc-port 플래그로 별도 TCP 포트에서 선택적 gRPC Inference·Control 서비스를 제공해요. 지정하지 않으면 gRPC 서버가 시작되지 않습니다. gRPC 리스너는 HTTP 서버와 같은 호스트 주소에 바인딩합니다.

경고: gRPC 인터페이스는 기본적으로 안전하지 않아요. 인증·권한 부여·암호화를 구현하지 않습니다. 신뢰할 수 있는 네트워크 내에서 함께 배치된 서비스 간에만 쓰는 사적 내부 인터페이스로 간주해야 해요. gRPC 포트를 공개 인터넷이나 신뢰할 수 없는 클라이언트에 노출하지 마세요. 활성화하면 방화벽 규칙, 네트워크 분할, 격리된 사설 네트워크 배포 같은 네트워크 수준 접근 제어로 보호하세요.

캐시 디렉터리 보안

vLLM은 자신의 캐시 디렉터리가 사적이고 신뢰할 수 있다고 가정해요. 캐시 내용은 암호화 무결성 검증 없이 로드되는데, 임의 코드 실행을 지원하는 형식도 포함됩니다. 신뢰할 수 없는 사용자나 프로세스가 vLLM의 캐시 디렉터리에 쓸 수 있다면 vLLM을 충돌시키거나 임의 코드를 실행하게 할 수 있어요.

vLLM 캐시 디렉터리를 신뢰할 수 없는 사용자와 공유하거나 신뢰할 수 없는 저장소에서 마운트하지 마세요. vLLM 설치 자체만큼 신중하게 캐시 디렉터리를 대하세요.

대부분의 캐시 경로는 단일 루트 아래의 하위 디렉터리로 기본 설정됩니다. VLLM_CACHE_ROOT를 바꾸면 그에서 상속하는 모든 기능의 기본 위치가 바뀌어요.

환경 변수 기본값 설명
VLLM_CACHE_ROOT ~/.cache/vllm 기본 캐시 디렉터리. XDG_CACHE_HOME이 설정되면 존중. 명시적 오버라이드가 없으면 아래 모든 경로가 여기서 상속
(torch.compile) $VLLM_CACHE_ROOT/torch_compile_cache/ AOT 컴파일 모델, Inductor 그래프, Triton 커널용 컴파일 캐시. VLLM_DISABLE_COMPILE_CACHE로 제어 (1로 설정하면 비활성화)
VLLM_FLASHINFER_AUTOTUNE_CACHE_DIR $VLLM_CACHE_ROOT/flashinfer_autotune_cache/<flashinfer-version>/<arch>/<cache-hash>/ FlashInfer autotune 구성 캐시
VLLM_ASSETS_CACHE $VLLM_CACHE_ROOT/assets/ 다운로드된 자산 (예: 토크나이저 파일)
VLLM_XLA_CACHE_PATH $VLLM_CACHE_ROOT/xla_cache/ XLA/TPU 컴파일 캐시
VLLM_MEDIA_CACHE (비활성화) 다운로드된 미디어(이미지, 비디오, 오디오)용 선택적 캐시. 명시적으로 설정하지 않으면 활성화되지 않음

권장사항: VLLM_CACHE_ROOT(및 컴파일 캐시가 비활성화된 경우 ~/.triton 같은 의존성이 쓰는 다른 캐시 디렉터리)의 파일 권한을 vLLM 프로세스 소유자만 읽고 쓸 수 있게 제한하세요. 신뢰할 수 없는 소스에서 캐시 내용을 복사하지 마세요. 컨테이너 배포 시 캐시 디렉터리를 마운트한다면 볼륨 소스가 신뢰할 수 있는지 확인하세요.

FIPS 호환성

FIPS 준수는 많은 요인에 달려 있어서 vLLM 배포가 자동으로 FIPS 준수가 되지는 않아요. 최근 변경으로 FIPS 지원 호스트에서 vLLM이 승인되지 않은 알고리즘이 차단될 때 충돌을 피하는 내성이 개선됐지만, 내성은 준수와 다릅니다. 배포가 FIPS 요구사항을 만족하는지는 호스트 OS, Python의 hashlib·ssl 모듈을 지원하는 OpenSSL 프로바이더, 설치된 선택적 의존성에 달려 있어요.

FIPS 관련 구성:

  • 멀티모달 입력 해싱--mm-hasher-algorithm(config 필드 mm_hasher_algorithm)은 기본값이 blake3인데 FIPS 승인 대상이 아님. FIPS 환경에서는 sha256 또는 sha512로 설정하세요.
  • 프리픽스 캐시 해싱--prefix-caching-hash-algo(config 필드 prefix_caching_hash_algo)를 sha256 또는 sha256_cbor로 설정하세요. xxhashxxhash_cbor 옵션은 FIPS 승인 대상이 아니에요.
  • TLS 암호화--ssl-ciphers를 사용해 API 서버의 TLS 핸드셰이크를 환경 정책과 일치하는 FIPS 승인 cipher suite로 제한하세요.

요약: 위 구성 노브들로 vLLM이 비승인 알고리즘을 피할 수 있고, 자동 폴백으로 FIPS 지원 호스트에서 충돌하지 않고 실행됩니다. 하지만 종단 간 FIPS 준수는 전체 배포의 속성 — 호스트 OS, crypto 프로바이더, 전이 의존성, 네트워크 아키텍처 — 이지 vLLM 단독의 속성이 아니에요.

Ray 클러스터 신뢰 모델과 환경 변수 전파

vLLM은 전체 Ray 클러스터를 하나의 신뢰 도메인으로 취급해요. Ray 클러스터 안에서 코드를 실행할 수 있는 모든 주체(예: actor나 task 제출)는 드라이버/API 서버 프로세스와 같은 수준의 신뢰를 가진 것으로 간주됩니다. Ray 클러스터 접근은 이미 워커 노드에서 완전한 코드 실행을 의미하므로, 환경 변수 전파만 제한하는 것은 의미 있는 보안 경계가 되지 못해요.

멀티 노드 배포에서 RayExecutorV2를 쓸 때 vLLM은 드라이버에서 원격 Ray 워커로 환경 변수를 전파해요. 전파는 get_driver_env_vars()의 copy-all-except-denylist 정책을 사용합니다. 드라이버의 os.environ에 있는 모든 환경 변수(워커별 변수 몇 개와 운영자가 명시적으로 제외한 이름 제외)가 워커로 보내져요. 워커 쪽에서는 setdefault 의미로 적용되어, 이미 있는 값을 덮어쓰지 않고 빠진 변수만 채웁니다.

하드닝 권장사항:

  1. 설정 파일로 denylist: $VLLM_CONFIG_ROOT/ray_non_carry_over_env_vars.json(기본 ~/.config/vllm/ray_non_carry_over_env_vars.json)에 전파에서 제외할 환경 변수 이름 배열을 담은 JSON 파일을 만드세요. 예: HF_TOKEN, AWS_SECRET_ACCESS_KEY, GOOGLE_APPLICATION_CREDENTIALS 등. 여기 나열된 변수는 드라이버에서 워커로 복사되지 않아요.
  2. 드라이버 환경 최소화: 드라이버 셸 환경에 자격 증명을 두는 대신 secrets manager, 마운트된 파일, 짧은 수명의 서브프로세스로 주입하세요.
  3. 네트워크·프로세스 격리: 워커 노드에서 procfs 가시성을 제한하고(/prochidepid=2로 마운트하거나 컨테이너 런타임으로 격리), 드라이버와 워커를 다른 OS 사용자나 비중첩 UID의 별도 컨테이너에서 실행하세요.
  4. Ray 클러스터 접근 제한: Ray의 TLS 인증으로 클러스터 멤버십을 제한하고, Ray 클러스터를 격리된 네트워크 세그먼트에 두며, Ray 클라이언트 포트나 대시보드를 신뢰할 수 없는 네트워크에 노출하지 마세요.

프리픽스 캐시 타이밍 부채널 완화 (캐시 솔팅)

프리픽스 캐싱은 공통 프롬프트 접두사를 공유하는 요청 간에 KV 캐시 블록을 재사용해요. 멀티 테넌트 배포에서 그 재사용은 타이밍 부채널(CVE-2025-46570)이 됩니다. 공격자는 Time to First Token(TTFT) 차이를 측정해 추측한 프롬프트 접두사가 다른 사용자의 캐시된 프롬프트와 일치하는지 추론할 수 있어요. 연구에 따르면 이 신호는 프리픽스 길이가 8토큰만 되어도 거의 완벽하게 구분됩니다(ROC AUC 0.99).

vLLM은 요청에 선택적 cache_salt 파라미터를 받아요. salt는 첫 KV 캐시 블록의 해시에 섞여, 같은 salt를 가진 요청만 캐시된 프리픽스 블록을 공유할 수 있습니다. cache_salt는 OpenAI 호환 chat completions, completions, responses, pooling(embeddings, classification, scoring) 엔드포인트와 Anthropic /v1/messages 엔드포인트에서 받아들여집니다.

OpenAI Python 클라이언트 사용:

response = client.chat.completions.create(
    model=model,
    messages=messages,
    extra_body={
        "cache_salt": "per-user-or-per-tenant-secret",
    },
)

salt 값을 어떻게 고를까: salt를 비밀로 취급하세요. 다른 테넌트가 쓰는 salt를 추측하거나 얻을 수 있는 공격자는 여전히 그 테넌트를 상대로 타이밍 공격을 수행할 수 있어요. 사용자 이름이나 계정 ID 같은 예측 가능한 식별자가 아니라 예측 불가능할 만큼 긴 무작위 값(예: base64 43자, 256비트)을 쓰세요.

권장사항:

  • 멀티 테넌트 배포: 모든 요청에 테넌트 경계를 적용할 비밀로 cache_salt를 설정하세요.
  • 단일 테넌트 배포: 캐시 솔팅은 불필요하며 최대 캐시 히트율을 위해 생략할 수 있어요.
  • 솔팅은 캐시 효율을 낮추는데, 캐시된 블록이 같은 salt를 가진 요청에서만 재사용되기 때문이에요. 프라이버시와 성능의 균형에 맞게 salt 값의 세분도를 고르세요.

멀티모달 미디어 UUID 보안

멀티모달 콘텐츠 부분(image_url, input_audio, video, image_embeds, audio_embeds, vision_chunk)은 선택적 uuid 필드를 받아요. 제공되면 vLLM은 원시 미디어 바이트를 해싱하는 대신 이 값을 미디어 항목의 캐시 식별자로 사용합니다. 이는 반복 요청에서 큰 미디어 페이로드를 재해싱하는 것을 피하고, 멀티모달 입력의 클라이언트 측 캐시 제어를 위한 기본 메커니즘이에요.

클라이언트 책임: 다른 사람이 추측할 수 없는 UUID를 생성하는 것은 클라이언트의 책임이에요. 순차 카운터, 짧은 문자열, 파일명이나 사용자 ID 같은 예측 가능한 식별자 대신 암호학적으로 무작위한 값 — 예를 들어 Python의 uuid.uuid4()를 통한 UUIDv4 — 을 사용하세요.

멀티 테넌트 위험: 같은 vLLM 서버를 공유하는 두 호출자가 다른 미디어에 같은 uuid를 제시하면 하나의 캐시 항목을 공유해요. 먼저 도착한 요청의 미디어가 둘 다에 제공되므로:

  • 무결성: 나중 호출자의 미디어가 조용히 버려지고 먼저 온 호출자의 캐시된 출력으로 대체됨
  • 기밀성: 다른 호출자의 UUID를 의도적으로 재사용하는 호출자가 그 호출자의 미디어에서 파생된 출력을 받음

cache_salt를 각 요청에 설정하면 추가 교차 테넌트 격리를 얻을 수 있어요. salt가 프리픽스 캐시 블록 해시에 섞이므로, UUID가 충돌해도 다른 salt의 요청은 캐시된 프리픽스 블록을 공유할 수 없습니다.

보안 취약점 신고

vLLM에서 보안 취약점을 발견했다고 생각되면 프로젝트의 보안 정책을 따라 신고하세요. 자세한 내용은 vLLM 보안 정책을 참고하세요.

더 알아보기 (Learn more)