Salesforce Agentforce 및 SalesBleed: Einstein Trust Layer의 기능과 한계
요약
Salesforce Agentforce와 SalesBleed 연구는 AI 에이전트 보안의 취약점을 분석합니다. 이 연구는 CRM 내부에 저장된 신뢰할 수 없는 텍스트가 프롬프트 주입을 통해 에이전트를 유도하고, 이를 이용해 DNS 조회를 거쳐 조직 외부로 데이터를 유출시킬 수 있음을 보여줍니다. 핵심은 최소 권한 원칙과 도구 범위 제한의 중요성입니다.
핵심 포인트
- 신뢰할 수 없는 텍스트가 프롬프트 주입을 통해 에이전트 행동을 유도할 수 있습니다.
- URL 파싱 결함은 DNS 조회를 강제하여 데이터 외부 유출 경로를 만듭니다.
- 에이전트 보안은 ID, 권한 위생 및 다운스트림 렌더링 방식에 달려있습니다.
- 최소 권한 원칙과 좁은 도구 범위 설정이 필수적입니다.
AI 에이전트 보안에 대한 논의는 빠르게 양극화되는 경향이 있습니다. 한쪽에서는 공급업체의 트러스트 레이어가 자율 워크플로우를 기본적으로 안전하게 만든다고 가정하는 반면, 다른 쪽에서는 입증된 모든 취약점을 CRM 내부에 에이전트를 배치하는 것이 근본적으로 작동 불가능하다는 증거로 간주합니다. "SalesBleed" 연구는 이 두 가지 관점 모두 실제 메커니즘을 놓치고 있음을 보여줍니다.
Zenity의 연구원들은 Salesforce의 멀티테넌트 경계를 우회하거나 필드 레벨 보안을 깨지 않았습니다. 대신, 그들은 평범한 CRM 레코드 내에 저장된 신뢰할 수 없는 텍스트가 에이전트를 이미 볼 권한이 있는 데이터를 가져오도록 유도할 수 있으며, URL 파싱과 기본 Slack 액션의 격차를 통해 해당 데이터가 DNS 조회(DNS lookups)를 거쳐 조직 외부로 나갈 수 있음을 보여주었습니다.
에이전트 보안은 보통 실질적인 ID 및 권한 위생 상태(identity and authorization hygiene)와 관련됩니다. 즉, 에이전트가 어떤 ID로 실행되는지, 해당 ID가 어떤 레코드와 필드에 접근할 수 있는지, 어떤 도구를 호출할 수 있는지, 그리고 다운스트림 클라이언트가 에이전트의 출력을 어떻게 렌더링하는지에 달려 있습니다.
요약
- 공개 Web-to-Lead 양식은 평범한 리드 레코드 내부에 숨겨진 프롬프트 주입(prompt-injection) 지침을 저장할 수 있습니다.
- 직원이 나중에 최근 리드에 대한 일상적인 질문을 Agentforce에게 했을 때, 에이전트는 저장된 텍스트를 지침으로 평가했습니다. 이로 인해 이미 읽을 권한이 있는 계정 데이터를 조회하도록 유도되었습니다.
- 테스트된 구성에서 신뢰할 수 있는 URL 파싱의 결함(quirk)은 추출된 계정 데이터를 외부 호스트 이름에 임베드할 수 있게 했습니다. 이미지 렌더링이나 Slack 링크 미리보기를 트리거하는 것이 해당 호스트 이름으로 DNS 조회를 강제했으며, 이를 통해 누군가 링크를 클릭하지 않아도 데이터가 유출되었습니다.
- 기본 Slack Knowledge 서브 에이전트의 관련 문제점은
Slack 스레드에 답장(Reply to a Slack Thread)기능을 사용하여 명시적인 사용자 확인이나 호출자 귀속 없이 메시지를 게시할 수 있게 했습니다. - Salesforce와 Zenity는 특정 플랫폼 측면의 문제가 공개되기 전에 패치되었음을 확인했습니다.
패치로 해당 경로들은 막았지만, 최소 권한(least-privilege) 설정과 좁은 도구 범위(narrow tool scopes)를 구성하는 것은 여전히 개별 조직 관리자(org admins)의 몫입니다.
공개 일정
Zenity는 2026년 6월 1일에 Salesforce에 발견 내용을 보고했습니다. Salesforce는 Trusted URLs 문제에 대한 수정 사항을 8월 18일에 확인했으며, Zenity는 8월 19일에 이를 검증했습니다. Slack 액션의 호출자 속성(Invoker attribution)은 8월 20일에 추가되었습니다. Zenity는 보고된 모든 수정 사항을 9월 21일까지 확인하고 9월 24일에 연구 결과를 발표했습니다.
SalesBleed는 공급업체가 출판에 앞서 시연된 플랫폼 버그들을 해결한 협력적 공개(coordinated disclosure)였습니다. 이 연구는 활발하고 광범위한 고객 침해의 증거라기보다는 유용한 개념 증명(proof of concept)이자 취약점 체인으로 기능합니다.
공격 작동 방식
데이터 유출 경로는 신뢰할 수 없는 사용자 입력, 기존 에이전트 권한, 그리고 다운스트림 렌더링에 의해 트리거된 자동 DNS 조회를 연결했습니다.
-
공격자가 리드를 제출했습니다. Web-to-Lead 양식은 설계상 인증되지 않습니다. Zenity는 일반적인 자유 형식 필드 안에 프롬프트 주입(prompt-injection) 지침을 삽입했습니다. 공격자는 Salesforce 자격 증명이 필요하지 않았습니다.
-
지침이 데이터베이스에 머물렀습니다. 페이로드(payload)가 즉시 실행될 필요는 없었습니다. 이는 직원이 최근 유입된 리드 요약과 같은 일반적인 질문을 Agentforce에게 할 때까지 기다렸습니다.
-
에이전트가 주입된 지침을 따랐습니다. Zenity의 테스트 환경에서, General CRM 서브 에이전트는 Leads와 Accounts 모두에 걸쳐
Query Records도구에 접근할 수 있었습니다. 주입된 프롬프트는 에이전트를 리드로부터 멀어지게 유도하여 회사 이름과 거래 규모를 포함한 계정 기록을 조회하도록 지시했습니다. 에이전트는 높은 권한이 필요하지 않았으며, 단지 이미 가지고 있던 권한을 사용했을 뿐입니다. -
데이터가 외부 호스트명에 추가되었습니다. 에이전트가 생성한 응답에는 쿼리된 CRM 데이터가 서브도메인으로 인코딩된 외부 URL이 포함되어 있었습니다.
이 수정 이전에는 Trusted URLs redaction과 다운스트림 렌더링 서피스(downstream rendering surfaces)가 특정 엣지 케이스 URL을 다르게 처리했습니다. 특히 흔하지 않은 최상위 도메인(top-level domains)이나 후행 문자(trailing characters)를 중심으로 그러했습니다. 리다크터(redactor)는 해당 문자열을 일반 텍스트로 취급했지만, 렌더링 클라이언트(rendering client)는 이를 활성 URL(live URL)로 구문 분석했습니다.
- 클라이언트 측 렌더러가 DNS 조회를 트리거했습니다. HTML 이미지 태그나 Slack 링크 미리보기가 URL을 렌더링하려고 시도할 때, 클라이언트는 공격자가 제어하는 호스트 이름(attacker-controlled hostname)을 해결하려고 시도했습니다. 이 DNS 쿼리는 HTTP 연결이 완료되기 전에 인코딩된 데이터를 직접 공격자의 네임서버(nameserver)로 전송했습니다. 방화벽에서 HTTP 트래픽을 차단하더라도 이미 완료된 DNS 쿼리를 막을 수는 없었습니다.
또한, 보조적인 Slack 피싱 벡터도 존재했습니다. 기본 Slack Knowledge 서브 에이전트의 'Slack 스레드에 답장하기(Reply to a Slack Thread)' 액션은 확인이나 누가 해당 에이전트를 호출했는지 표시할 필요가 없었습니다. 적대적인 리드(adversarial lead)는 에이전트에게 공식 봇인 것처럼 위장하여 내부 Slack 채널에 오해의 소지가 있는 메시지를 게시하도록 유도할 수 있었습니다. Salesforce는 이 수정 기간 동안 해당 액션의 기본 확인 및 출처 표기 동작을 업데이트했습니다.
여기서 '제로 클릭(zero-click)'이 의미하는 것
'제로 클릭'은 회사 내 누구도 행동을 취하지 않았다는 것을 의미하지 않습니다. 직원은 여전히 에이전트에게 평범한 질문을 해야 했고, 이로 인해 에이전트는 오염된 리드(poisoned lead)를 컨텍스트 윈도우(context window)로 가져오게 되었습니다.
제로 클릭 측면은 특히 데이터 유출 단계(exfiltration step)에 적용됩니다. 직원이 링크를 클릭하거나, 첨부 파일을 열거나, 예상치 못한 팝업을 승인할 필요가 없었습니다. 에이전트가 조작된 출력물(crafted output)을 생성하자마자, 렌더링 레이어(rendering layer)가 자체적으로 DNS 해석을 트리거했습니다.
연구가 보여주고 보여주지 않은 것
해당 연구는 cross-tenant exploit나 Salesforce의 핵심 권한 모델 우회는 입증하지 못했습니다. 에이전트는 자신이 합법적으로 볼 권리가 있는 레코드에 접근했습니다. 근본적인 문제는 비즈니스 레코드 내부에 존재하는 신뢰할 수 없는 데이터가 승인된 도구를 의도치 않은 쿼리로 유도하고, 그 데이터를 승인되지 않은 목적지로 라우팅할 수 있다는 점이었습니다.
공개 시연에서는 Web-to-Lead를 진입점으로 사용했습니다. 유사한 위험은 Email-to-Case, 인바운드 지원 티켓, 라이브 채팅 기록, 업로드된 문서에도 적용될 수 있지만, 이는 보고서에 명시된 특정 설정이라기보다는 관련된 위협 모델입니다.
또한, 여기서는 'DNS 터널링(DNS tunneling)'보다 'DNS 기반 유출(DNS-based exfiltration)'이라는 용어가 더 정확합니다. 해당 연구는 상호작용적인 양방향 DNS 터널이 아니라, 권한 있는 네임서버로 전송되는 표준 DNS 조회 요청을 통해 데이터가 외부로 나가는 것을 보여주었습니다.
Einstein Trust Layer가 실제로 다루는 범위
Salesforce는 Einstein Trust Layer를 데이터 검색 제어(data retrieval controls), 마스킹, 프롬프트 방어(prompt defenses), Trusted URLs, 감사 로그(audit logs)를 포함하는 생성형 기능에 대한 일련의 가드레일로 문서화합니다. 이러한 도구들은 워크플로우의 특정 부분에는 도움이 되지만, 에이전트 도구 실행을 위한 완전한 권한 정책 역할을 하지는 않습니다.
- 안전한 접지(Secure grounding) 및 Salesforce 권한: Salesforce는 실행하는 사용자의 접근 제어(access controls), 즉 개체 권한(object permissions), 공유 규칙(sharing rules), 필드 수준 보안(field-level security)을 따르도록 접지를 설계합니다. 하지만 이것이 과도하게 권한을 부여받은 ID를 해결하지는 못합니다. 만약 해당 ID가 특정 개체를 읽을 수 있다면, 에이전트는 일반적으로 요청받았을 때 그 개체에 대해 쿼리할 수 있습니다.
- 데이터 비보존(Zero Data Retention, ZDR): ZDR 계약은 OpenAI나 Azure와 같은 외부 모델 제공업체가 프롬프트를 보존하거나 학습에 사용하지 않도록 보장합니다. 이것은 운영 안전 가드레일(operational safety guardrail)이 아니라 공급업체 데이터 개인정보 보호 통제(vendor data privacy control)입니다. 이는 에이전트가 도구 실행 중 데이터를 어디로 보내는지와는 무관하며, Salesforce가 Data 360 내부에 프롬프트 기록 및 감사 로그를 저장하는 것을 막지 못합니다.
- 데이터 마스킹(Data masking): Trust Layer는 표준 생성형 기능에서 신용카드 번호나 사회 보장 번호 같은 인식 가능한 패턴을 마스킹할 수 있지만, Salesforce 문서는 현재 에이전트의 경우 데이터 마스킹이 비활성화되어 있다고 명시합니다. 배포 시에는 에이전트가 값을 처리하기 전에 민감한 값을 숨기기 위해 마스킹에 의존해서는 안 됩니다.
- 프롬프트 방어 및 주입 탐지(Prompt defense and injection detection): 의미론적 필터(Semantic filters)와 시스템 프롬프트는 명백한 오용을 플래그 지정하는 데 도움이 되지만, 확률적입니다. 방어적인 프롬프트 지침은 도구가 읽거나 실행할 수 있는 것에 대한 하드 리밋(hard limits)을 대체할 수는 없습니다.
- 신뢰할 수 있는 URL(Trusted URLs): 이 설정은 에이전트 출력에서 승인되지 않은 외부 도메인을 제거합니다. 이는 네트워크 방화벽이라기보다는 출력 필터 역할을 하며, 보고된 취약점은 URL 파서 불일치로 인해 호스트 이름 기반 조회(hostname-based lookups)가 빠져나갈 수 있음을 보여주었습니다. Salesforce는 이후 해당 구문 분석 동작을 수정했습니다.
- 감사 및 피드백(Audit and feedback): Data Cloud에 프롬프트, 모델 응답, 신뢰 신호를 기록하는 것은 이벤트 발생 후 가시성을 제공하지만, 감사 기록은 무단 조치가 실행되는 동안 이를 차단하지는 못합니다.
권한의 상충 관계 (The permission trade-offs)
Salesforce 문서는 Agentforce가 라이선스(licenses), 프로필(profiles), 권한 집합(permission sets), 공유 규칙(sharing rules)과 같은 표준 접근 제어 내에서 작동한다고 설명합니다. 에이전트는 할당된 컨텍스트 외부의 레코드에 자동으로 접근할 수 없습니다.
하지만, 권한을 준수하는 것이 좁은 권한을 가진 것과는 같지 않습니다. Zenity의 테스트 환경에서는 General CRM 서브 에이전트가 Query Records를 통해 Leads와 Accounts 모두에 접근할 수 있었습니다. 이 설정은 필드 수준 보안(field-level security) 자체의 실패라기보다는 광범위한 도구 구성의 위험성을 강조합니다.
런타임 ID(Runtime identities) 또한 설정에 따라 다릅니다:
- 직원 대상 에이전트: Lightning Experience, 모바일 앱 또는 Slack에서 이러한 에이전트는 일반적으로 로그인된 사용자의 권한으로 실행됩니다.
- 서비스 에이전트: 고객 대면 에이전트는 종종 전용 **에이전트 사용자(Agent User)**의 권한으로 실행되며, 이 사용자에 할당된 프로필과 권한 집합이 전체 데이터 접근을 결정합니다.
권한을 검토할 때 정확한 런타임 주체(runtime principal)를 확인하는 것이 일반적으로 가장 좋은 시작점입니다.
Agentforce를 위한 실용적인 권한 모델
에이전트를 설정하거나 감사할 때, 다음 네 가지 기본 질문에 답하는 것이 도움이 됩니다:
- 런타임 주체는 누구인가? 에이전트가 개별 직원, 전용 서비스 사용자 또는 통합 계정 중 어떤 자격으로 실행되는지 확인합니다.
- 어떤 객체에 접근할 수 있는가? 워크플로우가 필요로 하지 않는 객체에 대한 접근을 제거하고,
광범위한 쿼리 도구는 실제 운영상의 트레이드오프를 수반합니다. Salesforce는 사용자의 접근 권한을 기반으로 검색 가능한 표준 및 사용자 정의 객체를 검색하는 것을 Query Records로 문서화하고 있습니다. 공개 제출물을 처리하는 에이전트의 경우, 개방형 쿼리 동작을 특정 레코드 ID에 대해 특정 필드 세트만 반환하는 맞춤 Apex 액션 또는 Flow로 대체하는 것이 더 안전합니다.
맞춤 액션을 작성할 때, Apex 코드는 일반적으로 사용자 모드(WITH USER_MODE)에서 실행되어야 하며 공유 규칙(with sharing)을 강제하여 에이전트가 할당된 권한을 넘어 데이터를 쿼리하지 못하도록 해야 합니다.
더 안전한 리드 분류 워크플로우
더 안전한 워크플로우는 인증되지 않은 입력(unauthenticated inputs)을 내부 데이터베이스 및 아웃바운드 통신 도구로부터 격리합니다:
text
Public Web-to-Lead submission
...
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기