보안 및 권한 인식 RAG 배포 보장
빠르게 변화하는 인공지능 분야에서 Retrieval Augmented Generation (RAG)은 OpenAI의 GPT 시리즈와 Google의 Gemini 같은 생성 모델의 역량을 향상시키는 강력한 접근 방식으로 부상했습니다. 그러나 큰 잠재력에는 상당한 책임이 따르며, 특히 민감한 데이터를 보호하고 개인정보 보호 규정을 준수하는 데 있어 더욱 그렇습니다.
조직들이 AI 기반 솔루션에 점점 더 의존함에 따라, 이러한 기술의 보안 영향을 이해하는 것은 매우 중요합니다. 데이터를 보호할 뿐만 아니라 사용자 신뢰를 구축하는 강력한 보안 조치를 구현하는 것은 프로덕션 준비가 된 RAG 애플리케이션에 필수적입니다.
Zilliz가 주최한 최근 Unstructured Data Meetup에서 Opsin의 공동 창립자인 Oz Wasserman은 RAG 배포를 위한 핵심 보안 고려 사항을 강조하며, 데이터 익명화, 강력한 암호화, 입력/출력 검증, 견고한 접근 제어를 비롯한 중요한 보안 조치의 중요성을 강조했습니다.
이 블로그에서는 안전하고 권한을 인식하는 RAG 배포의 핵심 측면을 논의합니다. 또한 민감한 정보를 제거하도록 설계된 Milvus vector database와 LlamaIndex postprocessors를 사용한 RAG 파이프라인의 예제 노트북을 살펴보며, 데이터 개인정보 보호와 규정 준수를 보장합니다.
RAG 아키텍처 다이어그램
발표에서 Oz는 그림 1에 표시된 것과 유사한 기본 RAG 아키텍처를 설명하는 것으로 시작했습니다. 본질적으로 RAG 시스템은 사용자의 질의에 응답하여 관련 콘텐츠를 검색하기 위해 문서를 저장하는 vector database 기반 지식 베이스를 통합함으로써 large language model (LLM)을 향상시킵니다. 이 접근 방식은 정확도를 개선하고, 더 높은 맥락적 관련성을 보장하며, 독립형 LLM 출력에서 흔히 나타나는 hallucinations를 최소화합니다.
그러나 이 기본 파이프라인은 다양한 단계에 통합되지 않는 한 특정 보안 조치가 부족합니다. Oz는 DistributedApps.ai의 Ken Huang이 작성한 RAG 워크플로 차트(그림 2 참조)를 강조했으며, 여기서는 RAG 파이프라인 전반에 걸쳐 보안 제어를 구현할 수 있습니다:
데이터 소스/VectorDB 단계
검색 단계
생성 단계
Figure- Vector database facilitating RAG chatbot.png
그림 1: 기본 RAG 아키텍처
Figure 2- Detailed RAG Architecture (Author- Ken Huang)
그림 2: 상세 RAG 아키텍처(저자: Ken Huang)
데이터 소스/VectorDB 단계
Milvus와 Zilliz Cloud(관리형 Milvus) 같은 vector databases는 unstructured data에서 변환된 vector embeddings를 저장, 인덱싱, 검색합니다. 이들은 가치 있는 정보의 핵심 저장소입니다. 그러나 데이터 유출이나 무단 접근의 표적이 될 수도 있으므로 암호화, 접근 제어 또는 데이터 익명화와 같은 강력한 보호 전략의 구현이 필요합니다.
Figure 3- Data Source:VectorDB Security Controls.png
그림 3: 데이터 소스/VectorDB 보안 제어
첫 번째 보안 스레드는 데이터 익명화에서 찾을 수 있습니다. 데이터에는 일반적으로 개인 식별 정보(PII라고 불리는 개인 민감 정보가 포함되어 있습니다. 이 데이터는 개인의 프라이버시를 보호하기 위해 익명화되어야 합니다. 이 단계는 이 정보가 특정 개인에게 다시 추적될 수 없도록 보장하기 위해 모든 데이터 처리 전에 필수입니다.
데이터가 익명화되면, 관련 콘텐츠를 검색하기 위한 시맨틱 검색을 가능하게 하기 위해 인덱싱하고 임베딩을 생성할 수 있습니다. 이 단계에서는 누가 벡터 데이터베이스에 데이터를 저장하고 벡터 데이터베이스에서 데이터를 검색할 수 있는지, 즉 누가 이에 액세스할 수 있는지를 정의하는 것이 중요합니다. 엄격한 액세스 제어를 구현하는 것은 데이터 조작 또는 유출로 이어질 수 있는 무단 액세스를 방지하는 데 필수적입니다.
액세스 제어는 여러 단계로 나눌 수 있습니다:
인증: 사용자가 일반적으로 OAuth 2.0과 같은 방법을 사용하여 자신의 신원을 확인하도록 보장합니다.
권한 부여: 확인된 신원을 기반으로 특정 권한과 액세스 권리가 사용자에게 부여됩니다.
추적 가능성: 데이터에 액세스하려는 모든 시도가 기록되고 추적될 수 있도록 액세스를 모니터링하여 보안 컴플라이언스를 위한 감사 추적을 제공합니다.
이러한 조치는 벡터 데이터베이스를 보호하고 승인된 사용자만 민감한 데이터와 상호 작용할 수 있도록 보장하는 데 도움이 됩니다.
또 다른 보안 계층은 저장 상태(저장될 때) 또는 전송 중(전송될 때) 데이터가 이해할 수 없도록 만드는 암호화를 통해 추가될 수 있습니다. 전통적인 암호화는 암호화 키를 사용하지만, 차등 프라이버시 또는 분산화 및 샤딩과 같은 더 고급 기술도 데이터 보안을 강화하기 위해 점점 더 많이 도입되고 있습니다.
Zilliz Cloud는 Milvus로 구동되는 완전 관리형 벡터 데이터베이스 서비스입니다. 이는 이러한 모든 필수 데이터 보안 조치를 제공합니다. 또한 퍼블릭 인터넷 액세스를 피하기 위한 Private Link, 정기적이고 안전한 데이터 백업을 보장하기 위한 백업 및 복원, 데이터 손실 시 지식 베이스 복구와 같은 다른 솔루션도 제공할 수 있습니다.
Figure 6- Zilliz Multi-layered Enterprise-grade Security
그림 6: Zilliz 다층 엔터프라이즈급 보안 (출처)
검색 단계
검색 단계는 보안 문제를 해결해야 하는 또 다른 중요한 단계입니다. 이전 단계와 마찬가지로, 쿼리를 통해 지식 베이스에 대한 액세스를 제어하는 것이 필수적입니다. 또한 이 단계에서는 여러 보안 위험도 완화해야 합니다:
쿼리 검증: 쿼리를 검증하는 것은 프롬프트 인젝션 공격을 방지하는 데 매우 중요합니다. 이 접근 방식은 사용자 입력이 시스템 취약점을 악용하지 않도록 보장하여, 데이터에 대한 무단 액세스 또는 조작으로 이어질 가능성을 방지합니다.
유사도 검색 위험: 유사도 검색과 관련된 위험을 관리하는 것도 중요합니다. 유사도 검색이 의도치 않게 민감한 정보를 노출하거나 제한된 데이터에 대한 무단 액세스를 제공하지 않도록 적절한 조치가 마련되어야 합니다.
Figure 7- Query Validation Step
그림 7: 쿼리 검증 단계
Oz는 사내 모델을 발표하면서 프롬프트 인젝션의 예(그림 8 참조)를 공유했습니다. 이는 데이터를 검색하기 위해 프롬프트가 변경되는 프롬프트 조작의 좋은 예입니다.
그림 8- 프롬프트 인젝션의 예
그림 8: 프롬프트 인젝션의 예
또한 Oz는 유사도 검색과 관련된 여러 위험을 논의했으며, 여기에는 다음이 포함됩니다.
데이터 유출: 유사도 쿼리를 조작함으로써 공격자는 검색 메커니즘에 영향을 주어 민감한 데이터를 간접적으로 검색하게 할 수 있습니다.
검색 결과 조작: 공격자는 검색 프로세스를 수정하여 어떤 결과가 검색되는지에 영향을 줄 수 있으며, 이로 인해 제한된 정보가 잠재적으로 노출될 수 있습니다.
정찰 및 패턴 분석: 공격자는 검색 쿼리와 응답의 패턴을 분석하여 데이터베이스의 구조를 파악하고 저장된 데이터에 대한 인사이트를 얻을 수 있습니다.
리소스 고갈: 지속적이거나 과도한 쿼리는 서비스 거부 상태를 초래하여 시스템 리소스를 고갈시키고 다른 사용자의 가용성을 낮출 수 있습니다.
검색 단계에도 액세스 제어 또는 데이터 검증과 같은 유사한 보안 기법을 구현해야 함을 알 수 있습니다. 또한 구성 요소 간에 데이터가 전송되므로 암호화(전송 중)를 고려해야 합니다.
생성 단계
RAG 파이프라인의 마지막 단계는 생성 단계로, 대규모 언어 모델(LLM)이 벡터 데이터베이스에서 검색된 콘텐츠를 기반으로 응답을 생성합니다. 이 단계에서는 LLM이 중심 역할을 하지만, 모델 학습에 사용된 데이터의 성격에 따라 보안 및 컴플라이언스 문제가 발생할 수 있습니다. 주요 위험에는 다음이 포함됩니다.
데이터 개인정보 보호 위반: LLM이 개인 식별 정보(PII) 또는 기타 기밀 콘텐츠가 포함된 데이터로 학습된 경우, 민감하거나 개인적인 정보를 의도치 않게 노출할 수 있습니다. 이로 인해 GDPR 또는 HIPAA와 같은 개인정보 보호 규정 위반이 발생할 수 있습니다.
출력 조작: 공격자는 입력 쿼리에 영향을 주어 LLM의 출력을 조작할 수 있으며, 이는 악의적이거나 오해의 소지가 있는 콘텐츠 생성으로 이어질 수 있습니다. 이는 출력이 검증 없이 신뢰되는 환경에서 특히 우려됩니다.
편향 및 공격적인 콘텐츠: 학습 데이터에 편향되거나 공격적인 요소가 포함되어 있는 경우, LLM은 편향되거나 공격적인 응답을 생성할 수 있으며, 특히 프로덕션 환경에서 법적 책임으로 이어질 수 있습니다.
이러한 위험을 해결하려면 생성된 콘텐츠를 신중하게 후처리해야 하며, 여기에는 다음과 같은 기법이 포함됩니다.
콘텐츠 필터링: 민감하거나 편향되거나 공격적인 콘텐츠를 감지하고 차단하기 위해 출력을 자동으로 필터링합니다.
출력 검증: 사용자에게 전달하기 전에 모델의 응답이 보안 정책 및 규제 표준을 준수하는지 확인합니다.
모델 파인튜닝: 모델이 규정을 준수하는 데이터로 파인튜닝되도록 보장하고, 유해한 출력을 피하기 위해 강화 학습 전략을 적용합니다.
이러한 전략을 통합함으로써 생성 단계를 더 안전하고 신뢰할 수 있게 만들 수 있으며 보안 계층을 추가할 수 있습니다.
이제 다양한 LlamaIndex 후처리기를 비교하고 데이터 개인정보 보호 위반의 예를 보여주는 데이터 익명화 예제를 살펴보겠습니다.
LlamaIndex와 Milvus를 사용한 안전한 RAG
다음 노트북은 LLM 프레임워크로 LlamaIndex, 벡터 데이터베이스로 Milvus , 그리고 PII 마스킹에 특화된 세 가지 서로 다른 모듈을 사용하여 구축된 RAG 파이프라인의 예입니다. 하나는 Hugging Face NER 모델을 사용하고, 다른 하나는 LLM(OpenAI)과 Microsoft 라이브러리인 Presidio를 사용합니다.
전체 코드는 이 colab notebook에서도 확인할 수 있습니다.
Step 1: 환경 변수 설정
OpenAI 모델을 사용해 모듈을 테스트하려면 OpenAI 키가 필요합니다.
from google.colab import userdata
import os
os.environ["OPENAI_API_KEY"] = userdata.get('OPENAI_API_KEY')
Step 2: 개인정보가 포함된 텍스트 정의
먼저, 신용카드 번호, 이름, 생년월일과 같은 개인정보가 포함된 짧은 텍스트를 정의합니다.
from llama_index.core.postprocessor import NERPIINodePostprocessor
from llama_index.core.schema import TextNode, NodeWithScore
text = """
Hi, I'm Sarah Mitchell, and I just got a new credit card with the number 3714-496089-47322.
My personal email is sarah.mitchell@mailbox.com, and I'm currently based in Sydney.
By the way, I tried paying my utility bill with card number 6011-5832-9109-1726, but it didn't work.
For my bank transactions, I use this IBAN: NL91ABNA0417164300.
Also, can you help me with my Wi-Fi issues? I keep getting blocked by IP address 203.0.113.15.
I've shared a family photo on my personal blog at https://www.sarahs-lifediary.org/.
Oh, and my grandfather, George Stone, was born in 1921, while my grandmother, Emily Clarkson, was born in 1925.
Last question--what's the spending limit on my main card, the one ending in 8473?
"""
node = TextNode(text=text)
Step 3: PII 마스킹을 위한 NER 모델: NERPIINodePostprocessor
NERPIINodePostprocessor는 NER(개체명 인식)에 특화된 Hugging Face 모델을 사용해 해당 정보를 마스킹하는 Llama Index의 모듈입니다.
from llama_index.core.postprocessor import NERPIINodePostprocessor
from llama_index.core.schema import TextNode, NodeWithScore
processor = NERPIINodePostprocessor()
new_nodes = processor.postprocess_nodes([NodeWithScore(node=node)])
print(new_nodes[0].node.get_text())
이 특정 모델은 일부 정보를 마스킹하지만, 개인정보는 여전히 보입니다. 이 접근 방식을 사용하면 데이터 유출이 발생할 수 있습니다. 따라서 다른 모델이나 접근 방식을 사용해야 합니다.
"""
Output:
Hi, I'm [PER_9], and I just got a new credit card with the number 3714-496089-47322.
My personal email is sarah.mitchell@mailbox.com, and I'm currently based in [LOC_169].
By the way, I tried paying my utility bill with card number 6011-5832-9109-1726, but it didn't work.
For my bank transactions, I use this IBAN: NL91ABNA0417164300.
Also, can you help me with my Wi-[MISC_374] issues? I keep getting blocked by IP address 203.0.113.15.
I've shared a family photo on my personal blog at https://www.sarahs-lifediary.org/.
Oh, and my grandfather, [PER_545], was born in 1921, while my grandmother, [PER_599], was born in 1925.
Last question--what's the spending limit on my main card, the one ending in 8473?
"""
Step 4: PII 마스킹을 위한 LLM: PIINodePostprocessor
PIINodePostprocessor는 LLM 모델을 사용해 민감한 정보를 마스킹하는 Llama Index의 모듈입니다. 이 접근 방식의 효율성을 테스트하기 위해 기본 OpenAI 모델을 사용하겠습니다.
from llama_index.core.postprocessor import PIINodePostprocessor
from llama_index.core.schema import TextNode, NodeWithScore
from llama_index.llms.openai import OpenAI
processor = PIINodePostprocessor(llm=OpenAI())
new_nodes = processor.postprocess_nodes([NodeWithScore(node=node)])
print(new_nodes[0].node.get_text())
이 접근 방식을 따르면 모델은 모든 민감한 정보를 인식하고 그에 맞게 마스킹할 수 있습니다.
"""
Output:
안녕하세요, 저는 [NAME1] [NAME2]이고, 번호가 [CREDIT_CARD_NUMBER1]인 새 신용카드를 방금 받았습니다.
제 개인 이메일은 [EMAIL]이고, 현재 [CITY]에 거주하고 있습니다.
그런데 카드 번호 [CREDIT_CARD_NUMBER2]로 공과금을 결제하려고 했지만 작동하지 않았습니다.
은행 거래에는 이 IBAN을 사용합니다: [IBAN].
또한, 제 Wi-Fi 문제를 도와주실 수 있나요? IP 주소 [IP_ADDRESS] 때문에 계속 차단됩니다.
제 개인 블로그 [URL]에 가족사진을 공유했습니다.
아, 그리고 제 할아버지 [NAME3] [NAME4]는 [DATE1]에 태어나셨고, 제 할머니 [NAME5] [NAME6]는 [DATE2]에 태어나셨습니다.
마지막 질문--제 주 카드, 끝자리가 [CREDIT_CARD_ENDING]인 카드의 지출 한도는 얼마인가요?
"""
5단계: PII 마스킹을 위한 Presidio
마지막으로, NER에 특화된 Spacy 모델을 사용해 민감한 정보를 마스킹하는 Microsoft 라이브러리인 Presidio를 테스트합니다.
from llama_index.postprocessor.presidio import PresidioPIINodePostprocessor
from llama_index.core.schema import TextNode, NodeWithScore
from llama_index.llms.openai import OpenAI
processor = PresidioPIINodePostprocessor()
new_nodes = processor.postprocess_nodes([NodeWithScore(node=node)])
print(new_nodes[0].node.get_text())
이번에는 모델이 신용카드 번호를 제외한 모든 정보를 마스킹할 수 있음을 볼 수 있는데, 이는 해당 번호의 일부가 운전면허증으로 간주되기 때문입니다. 다음 단계에서는 이 모델로 여러 쿼리를 테스트하고, 신용카드 번호의 일부가 사용 가능하기 때문에 데이터 개인정보 침해가 어떻게 가능한지 보여드리겠습니다.
"""
출력:
안녕하세요, 저는 <PERSON_3>이고, 번호가 3714-<US_DRIVER_LICENSE_1>-47322인 새 신용카드를 방금 받았습니다.
제 개인 이메일은 <EMAIL_ADDRESS_1>이고, 현재 <LOCATION_1>에 거주하고 있습니다.
그런데 카드 번호 <IN_PAN_1>9109-1726로 공과금을 결제하려고 했지만 작동하지 않았습니다.
은행 거래에는 이 IBAN을 사용합니다: <IBAN_CODE_1>.
또한, 제 Wi-Fi 문제를 도와주실 수 있나요? IP 주소 <IP_ADDRESS_1> 때문에 계속 차단됩니다.
제 개인 블로그 <URL_1>에 가족사진을 공유했습니다.
아, 그리고 제 할아버지 <PERSON_2>는 <DATE_TIME_2>에 태어나셨고, 제 할머니 <PERSON_1>는 <DATE_TIME_1>에 태어나셨습니다.
마지막 질문--제 주 카드, 끝자리가 8473인 카드의 지출 한도는 얼마인가요?
"""
6단계: Milvus를 사용한 검색
먼저, 텍스트를 저장할 Milvus 벡터 데이터베이스를 만들어 보겠습니다.
from llama_index.core import VectorStoreIndex, StorageContext
from llama_index.vector_stores.milvus import MilvusVectorStore
vector_store = MilvusVectorStore(
uri="./milvus_demo.db", dim=1536, overwrite=True
)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex([n.node for n in new_nodes], storage_context=storage_context)
다음으로, 몇 가지 쿼리를 테스트해 보겠습니다.
이 첫 번째 쿼리는 제대로 작동합니다. 올바른 답변을 제공하고 정보를 마스킹된 상태로 유지합니다.
response = index.as_query_engine().query(
"What is the name of the person?"
)
print(str(response))
"""
출력:
그 사람의 이름은 <PERSON_3>입니다.
"""
이제 신용카드 번호를 가져오려고 해보면, 일부가 운전면허증으로 마스킹된 완전한 번호를 얻는 것을 볼 수 있습니다.
response = index.as_query_engine().query(
"What is the number of the credit card?"
)
print(str(response))
"""
출력:
신용카드 번호는 3714-<US_DRIVER_LICENSE_1>-47322입니다.
"""
하지만 모든 신용카드 발급사는 할당된 IIN(Issuer Identification Number)을 가지고 있다는 것도 알려져 있습니다. 첫 번째 숫자들은 발급사를 정의합니다. 따라서 이 경우 37은 American Express를 의미합니다. 이를 모른다면, 모델에 물어봄으로써 확인할 수 있습니다.
response = index.as_query_engine().query(
"What is the issuer of the credit card number?"
)
print(str(response))
신용카드 번호의 발급사는 American Express입니다
따라서, 텍스트에 이 정보가 제공되지 않았음에도 모델이 카드 발급사를 인식할 수 있음을 알 수 있습니다. 이는 모델이 발급사에 대한 신용카드 정보로 학습되었으며, 자체 지식을 바탕으로 답변을 제공할 수 있다는 뜻입니다. 이는 위에서 설명한 생성 단계의 데이터 프라이버시 침해의 예입니다.
결론
Oz는 RAG 파이프라인의 다양한 단계를 안내하며 보안 우려 사항을 해결할 수 있는 지점을 강조했습니다. 첫 번째 핵심 우려 사항은 데이터 익명화로, 민감한 데이터가 승인되지 않은 제3자에게 접근되지 않도록 보장하는 것입니다. 하지만 LLM 파운데이션 모델이 이러한 데이터로 학습되었을 수 있어, 모델 자체가 데이터 유출의 잠재적 원천이 될 수 있다는 점을 기억하는 것이 중요합니다.
데이터 접근 및 전송을 보호하기 위해서는 접근 제어와 암호화가 매우 중요합니다. 이러한 기술은 높은 수준의 보안과 제어를 제공하지만, 프롬프트 인젝션 및 검색 조작과 같은 위협은 여전히 민감한 정보에 대한 위험을 나타냅니다.
따라서 파이프라인 전반에 여러 보안 계층을 추가하는 것이 잠재적 위협을 완화하는 데 필수적입니다. 프로덕션 준비가 된 RAG 애플리케이션은 각 단계에서 강력한 보안 조치를 통합하여 시스템이 공격에 대해 회복력을 갖추고 규제 표준을 준수하도록 해야 합니다.
추가 자료
계속 읽기

My Wife Wanted Dior. I Spent $600 on Claude Code to Vibe-Code a 2M-Line Database Instead.
Write tests, not code reviews. How a test-first workflow with 6 parallel Claude Code sessions turns a 2M-line C++ codebase into a daily shipping pipeline.

A Developer's Guide to Exploring Milvus 2.6 Features on Zilliz Cloud
Milvus 2.6 marks a shift from “vector search + glue code” to a more advanced retrieval engine, and it is now Generally Available (GA) on Zilliz Cloud (a managed Milvus service).

Vector Databases vs. Key-Value Databases
Use a vector database for AI-powered similarity search; use a key-value database for high-throughput, low-latency simple data lookups.



