노출된 Cloud 함수의 위험과 강화 방법
Mandiant
*이 콘텐츠는 AI 번역 도구를 사용하여 번역되었습니다. 정확성을 기하기 위해 노력하고 있으나, 자동 번역에는 오류나 누락이 포함될 수 있습니다. 정확한 정보는 영문 원본을 참고해 주시기 바랍니다.
작성자: 코르네 드 용
소개
Mandiant 보안 평가에서는 특정 비즈니스 요구사항의 결과로 인증이 부족한 공개적으로 노출된 서버리스 애플리케이션을 자주 식별합니다. 서버리스 배포는 일반적으로 서드 파티 패키지를 통합하는 커스텀 개발 코드를 실행하므로 다음과 같은 광범위한 애플리케이션 수준 공격의 표적이 됩니다.
-
로컬 및 원격 파일 포함 (LFI/RFI)
-
명령어 삽입
이러한 취약점을 성공적으로 악용하면 공격자가 기본 컨테이너 인스턴스를 완전히 제어할 수 있습니다. 이러한 액세스는 궁극적으로 피해자의 클라우드 환경을 완전히 보안 침해하는 발판이 될 수 있습니다.
이 블로그 게시물에서는 고객 참여에서 얻은 교훈을 바탕으로 공격 시나리오를 설명하고 서버리스 환경을 보호하는 방법에 대한 실행 가능한 안내를 제공합니다. 이 분석은 공개적으로 액세스할 수 있어야 하는 Google Cloud Run 서비스 및 함수의 강화 전략에 중점을 두지만, 이러한 원칙은 모든 공개 서버리스 배포에 보편적으로 적용됩니다.
서버리스 애플리케이션이란 무엇인가요?
Function-as-a-Service (FaaS)라고도 하는 서버리스 애플리케이션을 사용하면 기본 인프라를 관리할 필요 없이 유연하고 분리된 이벤트 기반 클라우드 아키텍처 내에서 개별 코드 블록을 마이크로서비스로 배포할 수 있습니다. 이러한 서비스를 사용하면 애플리케이션과 자동화를 자동으로 확장하고 즉시 배포하여 운영 오버헤드를 제거할 수 있습니다. 서버리스 서비스는 주요 이커머스, 미디어, 결제 처리 애플리케이션, AI 사용의 기반이 됩니다.
생성형 AI 도입의 빠른 확산은 서버리스 아키텍처 사용 증가의 중요한 동인입니다. 챗봇 상호작용, 이미지 생성, '바이브 코딩', 다단계 AI 에이전트를 포함한 AI 워크플로는 서버리스 함수를 사용하여 사용자를 위한 작업을 완료합니다. 이러한 성장은 엔터프라이즈 보안팀에게 서버리스 환경을 보호해야 하는 더 시급한 과제를 안겨주었습니다.
서버리스 애플리케이션 공격의 위험
공개적으로 노출된 서버리스 워크로드는 위협 행위자의 초기 액세스 포인트 역할을 할 수 있습니다. 앞서 언급했듯이 이러한 서비스에는 코드, 가져온 패키지 또는 기본 런타임 환경 내에 취약점이 포함될 수 있습니다.
공격자는 진입점이 익스플로잇되면 일반적으로 권한을 에스컬레이션하거나 측면 이동을 시도합니다. 관찰된 일반적인 기법은 다음과 같습니다.
-
애플리케이션 코드 내에 직접 저장된 보안 비밀을 추출합니다.
-
애플리케이션 로직과 민감한 정보를 검토하여 환경 내에서 추가 공격 벡터를 식별합니다.
-
원격 코드 실행 (RCE)이 성공한 후 메타데이터 서버에서 서비스 계정 전달자 토큰을 무단 반출합니다.
보안 침해된 이러한 비밀번호 또는 서비스 계정을 활용하면 위협 행위자가 인접한 시스템과 워크로드로 피벗할 수 있으며, 적절한 강화 전략이 마련되어 있지 않은 경우 전체 환경을 장악할 수 있습니다.
공격 시나리오 예시
다음의 단순화된 시나리오에서는 서버리스 함수가 어떻게 보안 침해를 당할 수 있는지, 그리고 공격자가 초기 코드 실행을 달성한 후 어떻게 피벗하는지 보여줍니다.
로컬 파일 포함(LFI)
다음 Cloud Run 예시에서는 Python/Flask 함수가 적절한 검증을 수행하지 않고 파일을 열기 위해 사용자 제어 입력을 수락합니다. 이 패턴은 로컬 파일 포함 (LFI) 취약점의 예입니다.
import functions_framework
@functions_framework.http
def hello_http(request):
request_json = request.get_json(silent=True)
request_args = request.args
if request_json and 'file' in request_json:
file = request_json['file']
elif request_args and 'file' in request_args:
file = request_args['file']
# VULNERABILITY: The 'file' parameter is used directly in open()
# without validation, allowing arbitrary file access
with open(file, 'r') as resp:
filedata = resp.read()
return 'local file data {}!'.format(filedata)
그림 1: 검증되지 않은 사용자 입력을 수락하여 파일을 여는 취약한 Python/Flask 함수
이 취약점을 통해 공격자는 curl 을 사용하여 file 파라미터를 통해 POST 요청을 전송하여 Cloud Run 인스턴스에서 민감한 파일을 요청할 수 있습니다.
curl -X POST https://cloudrun01-abc.europe-west3.run.app/ -H "Content-Type: application/json" -d '{"file": "main.py"}'
그림 2: file 파라미터를 타겟팅하는 curl POST 요청
응답은 main.py 소스 코드 전체를 제공합니다. 공격자는 다음을 위해 코드를 분석할 수 있습니다.
-
API 키, 데이터베이스 사용자 인증 정보, 인증 토큰과 같은 하드 코딩된 보안 비밀
-
비즈니스 로직 결함 및 추가 삽입 지점
-
내부 서비스 엔드포인트 및 아키텍처 세부정보
-
기술 스택과 잠재적인 CVE 노출을 보여주는 import 문
또한 공격자는 표준 ../ 디렉터리 순회 시퀀스를 활용하여 민감한 시스템 파일을 검색할 수 있습니다.
curl -X POST https://cloudrun01-abc.europe-west3.run.app/ -H "Content-Type: application/json" -d '{"file": "../../../etc/passwd"}'
그림 3: 디렉터리 순회 시퀀스를 활용하는 curl POST 요청
LFI 취약점을 사용하면 공격자가 컨테이너에서 직접 다양한 파일을 가져오고 퍼징할 수 있습니다. 주요 예시는 다음과 같습니다.
-
requirements.txt, package.json, go.mod: 알려진 취약점이 있는 설치된 패키지와 버전을 식별하는 데 사용됩니다. -
.
env파일: 민감한 환경 변수 또는 하드 코딩된 보안 비밀을 포함하는 경우가 많습니다. -
애플리케이션 구성 파일: 안전하게 관리되지 않는 경우 데이터베이스 사용자 인증 정보, API 키 또는 서비스 엔드포인트를 포함할 수 있습니다.
-
/etc/passwd, /proc/self/environ: 사용자 정보, 환경 변수가 포함되어 있습니다. -
애플리케이션 로그: 인증 토큰 또는 PII 데이터가 포함될 수 있습니다.
권장사항: 소스 코드나 로컬 컨테이너 파일에 보안 비밀이나 사용자 인증 정보를 저장하지 마세요. Secret Manager와 같은 전용 보안 비밀 관리 솔루션을 활용합니다.
코드 실행/명령어 삽입
다음 시나리오에서는 Python 함수가 살균되지 않은 사용자 입력으로 셸 실행 메서드를 사용하여 공격자가 임의의 명령어를 실행할 수 있도록 허용합니다.
import functions_framework
import subprocess
@functions_framework.http
def hello_http(request):
request_json = request.get_json(silent=True)
request_args = request.args
if request_json and 'input' in request_json:
input = request_json['input']
elif request_args and 'input' in request_args:
input = request_args['input']
result = subprocess.run(input, shell=True,capture_output=True, text=True)
return format(result)
그림 4: 살균되지 않은 사용자 입력으로 셸 실행을 활용하는 Python 함수
이를 통해 공격자는 GCP 메타데이터 서비스를 타겟팅하는 후속 curl 요청을 실행하여 서비스 계정의 전달자 토큰을 검색할 수 있습니다.
다음 요청은 1시간 동안 유효한 서비스 계정의 OAuth 2.0 Bearer 토큰을 추출합니다.
curl -X POST https://cloudrun02-abc.europe-west3.run.app/ -H "Content-Type: application/json" -d "{\"input\": \"curl 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token' -H 'Metadata-Flavor: Google'\"}"
그림 5: curl 요청을 통한 GCP 서비스 계정 전달자 토큰 추출
공격자는 획득한 사용자 인증 정보를 공격자가 제어하는 시스템에서 사용하여 Google Cloud CLI 명령어를 실행할 수 있습니다. 예를 들어 도용된 전달자 토큰을 사용하여 CLOUDSDK_AUTH_ACCESS_TOKEN 환경 변수를 설정할 수 있습니다.
export CLOUDSDK_AUTH_ACCESS_TOKEN=”obtain bearer token”
그림 6: CLOUDSDK_AUTH_ACCESS_TOKEN 환경 변수 정의
그러면 공격자는 Cloud Run Compute 서비스 계정의 보안 컨텍스트 내에서 Google Cloud Cloud CLI를 활용할 수 있습니다. 예를 들어 Cloud Run 서비스가 편집자 권한이 있는 기본 컴퓨팅 서비스 계정으로 실행되는 경우, 권장사항과 신중한 구성 제어 없이 배포하면 전체 GCP 프로젝트를 장악하는 것과 동일한 효과를 내며 공격자가 다음을 수행할 수 있습니다.
-
대부분의 GCP 리소스 읽기/쓰기/삭제
-
새로운 서비스를 배포하고 기존 구성을 수정합니다.
-
보안 비밀 및 암호화 키에 액세스
-
액세스 가능한 모든 스토리지 시스템에서 데이터 유출
-
새 서비스 계정 또는 SSH 키를 통해 영구 백도어를 설정합니다.
강화 권장사항
Mandiant는 효과적인 서버리스 보안을 위해 조직에서 병행 접근방식을 구현할 것을 권장합니다.
-
안전한 소프트웨어 개발 수명 주기 (S-SDLC): 배포 전에 보안 스캔, 코드 검토, 최소 권한 IAM을 CI/CD 파이프라인에 통합하고 지속적인 보안 테스트를 통합합니다.
-
바이브 코딩: Mandiant는 AI로 생성된 코드 또는 '바이브 코딩'에 대해 다층 보안 적용을 권장합니다. 조직은 전용 샌드박스 환경 내에서 AI 실험을 격리하고 엄격한 데이터 이그레스 제어를 적용하여 프로덕션 시스템과 내부 데이터를 보호해야 합니다. 또한 개발 환경은 승인된 IDE로 제한되어야 하며, 공급망 취약점을 완화하기 위해 최소 권한으로 작동하는 검증된 플러그인만 활용하는 인간 중심 루프 기능을 갖춰야 합니다. 마지막으로 조직은 허용된 사용 사례에 관한 명확한 내부 가이드라인을 수립하는 동시에 AI로 생성된 소프트웨어가 안전한 소프트웨어 개발 수명 주기 (S-SDLC) 제어를 따르도록 보장해야 합니다. Vibe 코딩을 위한 포괄적인 보안 기본사항은 Wiz Vibe 코딩 보안 기본사항 블로그에 자세히 설명되어 있습니다.
-
보상 런타임 제어: 애플리케이션 취약점이 존재하더라도 보안 침해를 제한하고 억제하기 위해 다음과 같은 심층 방어 조치를 구현합니다.
공공 서비스 분리
신뢰할 수 없는 외부 엔터티가 사용하는 공개 Cloud Run 서비스를 전용의 격리된 Google Cloud 프로젝트에서 호스팅합니다. 이렇게 하면 보안 침해로 인해 중요한 내부 리소스에 즉시 액세스할 수 없게 됩니다. 이 '서비스 프로젝트' 모델의 구현은 이 게시물의 범위를 벗어나지만 보안 서버리스 아키텍처 청사진에 자세히 설명되어 있습니다.
Identity and Access Management(IAM)
Mandiant는 최소 권한의 원칙에 따라 기본 Compute Engine 서비스 계정 대신 서비스 인증을 위한 커스텀 서비스 계정을 사용할 것을 권장합니다. Cloud Run 함수가 작동하는 데 필요한 특정 권한만 부여합니다(예:
-
Cloud Storage 버킷 액세스: 서비스에 Cloud Storage 버킷의 객체에 대한 읽기 액세스만 필요한 경우 해당 특정 버킷으로 제한된 스토리지 객체 뷰어 (
roles/storage.objectViewer) 역할을 부여합니다. -
Secret Manager 액세스: 서비스에 보안 비밀에 대한 액세스가 필요한 경우 Secret Manager 보안 비밀 접근자 (roles/secretmanager.secretAccessor) 역할을 필요한 개별 보안 비밀에만 부여합니다. Cloud Run에서 보안 비밀에 액세스하는 방법에 대한 자세한 내용은 보안 비밀 구성에 관한 GCP 문서를 참조하세요.
레이어 7 애플리케이션 부하 분산기 (ALB) 아키텍처
서버리스 함수의 인그레스 트래픽을 내부 전용으로 제한하고 외부 Layer 7 ALB를 사용하여 인터넷 노출을 관리합니다. 다음과 같은 이점을 제공합니다.
-
중앙 집중식 트래픽 관리: 헤더 및 SSL 정책에 대한 세분화된 제어
-
Cloud Armor 통합: 로컬/원격 파일 포함 (LFI/RFI) 및 서버 측 요청 위조 (SSRF)와 같은 취약점에 대해 애플리케이션을 강화하는 웹 애플리케이션 방화벽 (WAF) 지원
-
트래픽 형태: 악용을 방지하기 위해 속도 제한 및 요청 제한을 구현합니다.
-
향상된 가시성: 보안 모니터링을 위한 강력한 로깅 및 로그 전달 기능
-
IAP (Identity-Aware Proxy): 내부 사용자를 위한 특정 ID 기반 인증이 필요한 시나리오를 위한 통합 지원
웹 애플리케이션 방화벽 (WAF) — Cloud Armor
Cloud Armor는 부하 분산기와 통합하여 악성 트래픽을 필터링할 수 있는 WAF 보호 기능을 제공합니다. 다음 예시는 앞에서 설명한 특정 로컬 파일 포함, 원격 코드 실행, 순회 공격을 차단하도록 Cloud Armor 보안 정책을 구성하는 방법을 보여줍니다.
로컬 파일 포함
lfi-v33-stable 사전 구성된 WAF 규칙은 일반적인 로컬 파일 포함 공격을 차단할 수 있습니다 (로컬 파일 포함 참조).
evaluatePreconfiguredWaf('lfi-v33-stable', {'sensitivity': 3})
그림 7: Cloud Armor lfi-v33-stable WAF 규칙 구성
경로 순회 요청 ../../../etc/passwd 를 차단하여 403 금지 오류 발생:
curl -X POST https://exampleabc01.com -H "Content-Type: application/json" -d '{"file": "../../../etc/passwd}'
<!doctype html><meta charset="utf-8"><meta name=viewport content="width=device-width, initial-scale=1"><title>403</title>403 Forbidden
그림 8: Cloud Armor가 경로 순회 요청을 차단하여 403 금지 오류가 발생하는지 확인
원격 코드 실행
rce-v33-stable 사전 구성된 WAF 규칙은 원격 코드 실행 시도를 차단할 수 있습니다 (원격 코드 실행 참조).
evaluatePreconfiguredWaf('rce-v33-stable', {'sensitivity': 3})
그림 9: Cloud Armor rce-v33-stable WAF 규칙 구성
이전 예시의 원격 코드 실행 요청을 차단하면 403 금지 오류가 발생합니다.
curl -X POST https://exampleabc01.com -H "Contencurl -X POST https://exampleabc01.com -H "Content-Type: application/json" -d "{\"input\": \"curl 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token' -H 'Metadata-Flavor: Google'\"}"
<!doctype html><meta charset="utf-8"><meta name=viewport content="width=device-width, initial-scale=1"><title>403</title>403 Forbidden
그림 10: Cloud Armor가 원격 코드 실행을 차단하여 403 Forbidden이 발생하는지 확인
서버리스 아키텍처 제어
Cloud Run 서비스 강화는 안전한 아키텍처의 일부일 뿐입니다. 이러한 서비스는 다른 Google Cloud 리소스에 연결되는 경우가 많으므로 단일 보안 침해로 인해 추가 서비스가 노출될 수 있습니다. 심층 방어를 구현하는 것이 중요합니다. 특히 직접 VPC 이그레스 또는 VPC 액세스 커넥터를 사용하는 경우 VPC 서비스 제어를 사용하여 세분화된 액세스 정책을 통해 수평 이동 및 무단 반출을 제한합니다.
안전한 소프트웨어 개발 수명 주기 (S-SDLC)
앞서 설명한 강화 전략도 중요하지만, 이상적인 표준은 초기 개발 단계에서 취약점을 선제적으로 식별하는 것입니다. 기존 코드 내에서 위험을 완화하는 데 초점을 맞춘 이 분석에서는 'Shift-Left' 보안에 대한 심층적인 분석은 다루지 않습니다. 그러나 안전한 소프트웨어 개발 수명 주기 (S-SDLC)는 여전히 기본 원칙입니다. 서버리스 함수가 외부로 게시되기 전에 위협을 무력화하려면 강력한 코드 검증과 지속적인 보안 테스트가 필수적입니다.
Cloud Run Threat Detection
이 게시물에 설명된 강화 권장사항 외에도 Google Cloud Security Command Center (SCC)는 Cloud Run 리소스에 대한 컨트롤 플레인 공격을 감지하는 기본 제공 서비스를 제공합니다. 여기에는 사용자 인증 정보 액세스, 정찰, 스크립트 또는 리버스 셸 실행을 위한 감지기가 포함됩니다. Cloud Run Threat Detection 서비스는 프리미엄 및 Enterprise 등급에서 사용할 수 있습니다.
결론
서버리스 애플리케이션은 민첩성과 빠른 비즈니스 가치를 실현합니다. '바이브 코딩'으로 코드를 그 어느 때보다 쉽게 배포할 수 있게 되었지만, 이러한 빠른 속도에 발맞추기 위해서는 팀이 개발 수명 주기 초기에 보안을 통합하고, 기본 구성을 넘어, ID와 아키텍처를 중심으로 심층 방어 전략을 우선시해야 합니다.
감사의 말씀
이 분석은 Ischa Rijff, Phil Pearce, Juraj Sucik의 도움이 없었다면 가능하지 않았을 것입니다.
