Skip to main content

22장. 프롬프트 인젝션과 안전성

5부. 서비스 품질과 운영 / 22장. 프롬프트 인젝션과 안전성

텍스트를 받는 순간 생기는 새로운 공격면

기존 웹 서비스에서는 코드와 데이터의 경계가 비교적 또렷했다. SQL은 SQL이고, 사용자 입력은 입력이었다. 하지만 LLM 서비스에서는 사정이 다르다. 모델은 시스템 지시, 사용자 질문, 검색된 문서, 이메일 본문, 웹페이지 텍스트를 거의 같은 재료처럼 읽는다. 사람 눈에는 그냥 참고 자료로 보이는 문장도, 모델에게는 새로운 명령처럼 들릴 수 있다. 그래서 프롬프트 인젝션은 단순한 입력값 검증 문제를 넘어, LLM 서비스 전체의 신뢰 경계를 다시 그리게 만드는 대표적인 취약점이 됐다.1

가장 익숙한 형태는 직접 인젝션이다. 사용자가 대놓고 이전 지시를 무시하라고 말하거나, 다른 역할을 연기하라고 요구하는 방식이다. 이것만 보면 그나마 단순하다. 더 위험한 것은 간접 인젝션이다. 사용자가 직접 공격 문장을 쓰지 않아도, 모델이 읽는 외부 문서 안에 숨겨진 지시가 시스템을 흔들 수 있다. 예를 들어 요약 기능이 읽는 PDF, 메일 비서가 처리하는 이메일, 브라우징 에이전트가 방문한 웹페이지, 심지어 화면 속 이미지 텍스트까지 공격 경로가 될 수 있다.12

여기서 중요한 사실이 하나 있다. 프롬프트 인젝션은 아직 완전히 해결된 문제가 아니다. 즉, 어느 한 줄의 프롬프트나 어느 한 개의 필터로 깔끔하게 끝나는 문제가 아니라는 뜻이다. 그래서 실무에서는 막아낸다보다 줄인다, 버틴다, 안전하게 실패한다는 사고방식이 더 현실적이다.1

문제는 답변이 아니라 권한이다

같은 공격이라도 서비스 구조에 따라 피해 규모는 완전히 달라진다. 단순 채팅 봇이 이상한 답변을 하는 정도라면 불쾌하고 곤란할 수는 있어도 피해가 제한적이다. 하지만 모델이 사내 문서를 읽고, CRM을 조회하고, 환불을 요청하고, 이메일을 보내고, 외부 API를 호출할 수 있다면 이야기가 달라진다. 그때부터 프롬프트 인젝션은 말장난이 아니라 권한 탈취에 가까워진다.

이 때문에 시스템 프롬프트는 보안 경계가 아니라 행동 가이드에 가깝다고 이해해야 한다. 시스템 프롬프트에 환불 금지라고 써 두었다고 해서 환불 권한 통제가 끝난 것이 아니다. 진짜 통제는 애플리케이션 계층에서 해야 한다. 사용자 권한 확인, 자원 접근 제한, 파라미터 검증, 승인 절차 같은 전통적인 제어가 여전히 핵심이다.

실제로 여러 벤더의 모델에서 비슷한 문법의 탈옥 기법이 반복적으로 통했다는 보고도 있었다. 이것이 의미하는 바는 간단하다. 특정 모델 제공사의 안전장치만 믿고 설계하면 안 된다는 것이다. 모델은 방어선의 일부일 뿐, 마지막 성벽이 아니다.3

안전하게 실패하는 서비스 설계

안전한 LLM 서비스는 공격을 한 번도 허용하지 않는 서비스가 아니다. 현실적으로 더 중요한 것은, 모델이 헷갈리거나 속았을 때도 큰 사고로 이어지지 않도록 만드는 것이다. 안전하게 실패한다는 말은 바로 그 뜻이다.

실무에서는 다음 다섯 가지 원칙이 뼈대가 된다.

  • 신뢰하지 않고 시작한다. 사용자 입력뿐 아니라 검색 문서, 파일 내용, 웹페이지, 이메일, 툴 결과까지 모두 잠재적으로 불신한다.

  • 최소 권한만 준다. 모델이 해야 할 일에 꼭 필요한 데이터와 도구만 허용한다.

  • 실패하면 차단한다. 분류기가 애매하다고 말하거나 스키마 검증이 실패하면 그냥 진행하지 않는다.

  • 고위험 행동은 사람을 거친다. 송금, 삭제, 메일 발송, 권한 변경 같은 행동은 사람이 최종 확인한다.

  • 문맥을 분리한다. 불신 입력을 읽는 단계와 실제 행동을 결정하는 단계를 분리해 한 번에 모든 권한이 이어지지 않게 한다.

이 다섯 가지를 지키면 서비스는 조금 덜 화려해질 수 있다. 대신 훨씬 덜 위험해진다. 실전에서는 그 차이가 매우 크다. 사용자가 한 번 웃고 지나갈 해프닝과, 새벽에 보안팀이 호출되는 사고를 가르는 선이 바로 여기에 있다.

입력 단계 방어: 데이터와 지시를 섞지 마라

입력 방어의 첫 원칙은 간단하다. 신뢰된 지시와 신뢰되지 않은 데이터를 한 덩어리로 섞지 않는 것이다. 시스템 지시와 사용자 입력은 역할을 분리해 전달하고, 검색된 문서나 업로드 파일은 명확한 구분자 안에 넣어 이것은 따라야 할 명령이 아니라 처리할 데이터라고 모델에 반복해서 알려줘야 한다.2

물론 프롬프트 문구만으로 충분하지는 않다. 입력 자체도 손봐야 한다. 지나치게 긴 입력은 잘라야 하고, 숨은 HTML 주석, 제로 폭 문자, 눈에 잘 보이지 않는 텍스트, 이상한 인코딩, 역할 전환을 유도하는 문구는 정규화와 탐지 과정을 거쳐야 한다. 공격자는 늘 정직하게 이전 지시를 무시하라고 쓰지 않는다. 글자를 흩어 쓰거나, 번역 요청으로 감추거나, Base64처럼 인코딩해 우회하기도 한다. 그러니 패턴 매칭은 필요하지만, 절대 충분하지 않다.12

RAG에서는 한 가지를 더 해야 한다. 검색된 문서를 곧바로 본 모델에 먹이지 말고, 먼저 정제하거나 격리된 단계에서 검토하는 것이다. 특히 외부 위키, 고객 업로드 문서, 웹페이지, 이메일은 원문 자체가 공격 페이로드일 수 있다. 이때 유용한 방법이 문서 정화다. HTML과 스크립트성 마크업을 벗기고, 숨은 텍스트를 제거하고, 허용된 메타데이터만 남기고, 필요한 청크만 추려서 전달한다. 문서를 요약하는 모델과 실제 행동을 결정하는 모델을 분리하는 것도 좋은 방법이다.

프롬프트 단계 방어: 시스템 프롬프트는 비밀 금고가 아니다

많은 팀이 시스템 프롬프트를 만능 방패처럼 다룬다. 하지만 시스템 프롬프트는 노출될 수 있다고 가정하는 편이 안전하다. 따라서 그 안에는 절대로 비밀번호, API 키, 내부 토큰, 민감한 운영 절차 같은 진짜 비밀을 넣으면 안 된다. 노출되어도 서비스가 무너지지 않는 정보만 들어 있어야 한다.

프롬프트 자체는 단단하게 만들 수 있다. 역할을 분명히 고정하고, 상충하는 지시는 따르지 말라고 못 박고, 사용자 데이터는 데이터로만 다루라고 여러 번 상기시키는 방식이 대표적이다. 흔히 샌드위치 방어라고 부르는 구조처럼, 사용자 입력 뒤에 다시 핵심 제한을 재확인하는 패턴도 실무에서 자주 쓴다.2

하지만 여기서 멈추면 안 된다. 프롬프트는 설득이지 강제 실행이 아니기 때문이다. 예를 들어 대화형 환불 도우미를 만든다면, 프롬프트에 승인 없이 환불하지 말라고 쓰는 것만으로는 부족하다. 실제 환불 API는 서버가 사용자 권한, 주문 상태, 금액 한도, 승인 여부를 확인한 뒤에만 호출돼야 한다. 프롬프트는 규칙 설명서이고, 집행은 애플리케이션이 맡아야 한다.

또 하나 놓치기 쉬운 부분이 API 계층 검증이다. 일부 공격은 메시지 구조 자체를 악용한다. 예를 들어 미리 채운 assistant 응답 같은 기능이 남용되면 모델이 이미 시작된 답변을 이어 쓰려는 성질 때문에 안전장치가 흔들릴 수 있다는 보고가 있었다. 그래서 서버는 요청 메시지의 역할 순서와 허용 필드를 검증해야 하며, 클라이언트가 assistant 역할 내용을 임의로 밀어 넣지 못하게 해야 한다.6

출력 단계 방어: 보기 전에 검사하고, 실행 전에 더 엄격하게 검사하라

입력을 잘 막아도 출력이 새면 소용없다. LLM 보안에서 출력 검사는 선택이 아니라 필수다. 특히 서비스가 사람에게 보여 주는 답변과 시스템이 그대로 실행할 명령은 같은 수준으로 다루면 안 된다.

사람에게 보여 주는 응답은 최소한 세 가지를 확인해야 한다. 민감정보가 섞였는지, 유해하거나 위험한 내용이 포함됐는지, 의도한 형식과 범위를 벗어났는지다. 주민등록번호, 카드번호, 비밀번호 형식의 문자열, 내부 식별자, 시스템 프롬프트 일부가 보이면 마스킹하거나 차단해야 한다. 데이터 유출은 해킹처럼 요란하게 일어나지 않고, 멀쩡한 답변 속에 조용히 섞여 나가는 경우가 많다.4

시스템이 실행할 출력은 훨씬 더 엄격해야 한다. 가장 좋은 방법은 구조화 출력과 스키마 검증을 거치는 것이다. 모델이 자연어로 아마 이걸 하라는 뜻입니다라고 말하게 두지 말고, 허용된 필드만 가진 JSON 같은 제한된 형식으로 의도를 제출하게 해야 한다. 그리고 그 결과를 서버가 다시 검증해야 한다. 필수 필드가 비었거나, 허용되지 않은 액션이 들어 있거나, 파라미터 범위를 벗어나면 그냥 버려야 한다. 다시 말해 모델 출력은 명령이 아니라 제안서다.

렌더링도 조심해야 한다. 응답을 마크다운이나 HTML로 그대로 보여 주면 링크, 이미지, 스크립트성 요소를 통해 또 다른 문제가 생길 수 있다. 특히 클릭 가능한 링크나 외부 리소스 자동 로딩은 데이터 유출 경로가 될 수 있다. 표시용 텍스트와 실행 가능한 요소를 분리하는 습관이 필요하다.

도구 호출 방어: 모델에게 만능 리모컨을 주지 마라

툴 호출은 LLM 서비스를 가장 강력하게도 만들고 가장 위험하게도 만드는 기능이다. 모델이 캘린더를 읽고, 메일을 보내고, 결제를 만들고, 사내 DB를 조회하는 순간부터 공격 표면은 한 단계가 아니라 여러 단계 커진다.

가장 중요한 원칙은 허용 목록 기반 제어다. 모델이 아무 도구나 마음대로 고르지 못하게 하고, 현재 작업에 필요한 도구만 열어 줘야 한다. 읽기 전용으로 충분한데 쓰기 권한까지 주면 안 된다. 사용자별, 테넌트별, 작업별로 스코프가 좁은 토큰을 발급하고, 토큰 수명도 짧게 가져가는 편이 안전하다.2

도구 파라미터도 검증해야 한다. 예를 들어 send_email이라는 도구가 있다고 해 보자. 모델이 이메일을 보내자고 결정했다고 해서 곧바로 보내면 안 된다. 수신자 도메인이 허용 목록에 있는지, 제목과 본문에 민감정보가 포함됐는지, 첨부 파일이 정책에 맞는지, 현재 사용자가 그 행동을 할 권한이 있는지 다시 검사해야 한다. 이때 검사 로직은 반드시 모델 바깥에 있어야 한다.

고위험 작업에는 사람 승인도 필요하다. 삭제, 환불, 계정 비활성화, 외부 발송, 데이터 내보내기 같은 행동은 확인 창 하나가 귀찮아 보여도 그 확인 창이 회사를 살릴 수 있다. 자동화의 멋보다 사고의 크기를 먼저 계산해야 한다.

여기서 한 걸음 더 나가면 계획과 실행을 분리하는 구조가 유용하다. 첫 번째 모델은 할 일을 제안만 하고, 두 번째 단계는 규칙 엔진이나 서버 로직이 실제 실행 가능 여부를 판단한다. 모델이 똑똑해질수록 오히려 이 분리는 더 중요해진다.

RAG와 에이전트에서 특히 조심할 것

RAG와 에이전트는 LLM 보안에서 가장 사고가 커지기 쉬운 구간이다. 이유는 단순하다. 외부 세계와 연결되기 때문이다.

RAG에서는 검색 품질만 보지 말고 검색 위생도 봐야 한다. 신뢰된 저장소를 우선하고, 문서별 신뢰 등급을 두고, 관리자 승인 없이 올라온 문서는 격리 구역에서 먼저 검사하는 편이 좋다. 문서에 숨은 지시가 있는지 탐지하고, 이상 패턴이 보이면 검색 결과에서 제외하거나 경고를 붙여야 한다. 검색된 문서는 참고 근거이지 상위 명령이 아니라는 사실을 프롬프트와 애플리케이션 양쪽에서 일관되게 유지해야 한다.15

에이전트에서는 툴 결과도 불신해야 한다. 많은 팀이 사용자 입력은 검사하면서 툴 응답은 믿어 버린다. 하지만 외부 API 응답, 웹 검색 결과, 스크린샷 OCR 텍스트도 모두 공격면이다. 브라우징 에이전트가 읽은 웹페이지가 새로운 지시를 심어 넣을 수 있고, 화면을 읽는 멀티모달 모델은 이미지 속 작은 텍스트에도 영향을 받을 수 있다. 그러니 툴 출력도 다시 정화하고, 필요하면 별도 분류기를 통과시켜야 한다.1

데이터 유출은 조용하게 일어난다

프롬프트 인젝션이 시끄러운 공격이라면 데이터 유출은 조용한 사고다. 모델이 시스템 프롬프트 일부를 흘리거나, 다른 사용자의 문맥을 섞거나, 검색된 비공개 문서에서 민감한 단락을 그대로 꺼내거나, 사용자가 프롬프트에 붙여 넣은 비밀키가 로그에 남는 식이다. 별다른 에러 없이 서비스는 멀쩡해 보일 수 있다. 그래서 더 위험하다.4

예방의 핵심은 데이터 최소화다. 모델에 보내는 문맥은 필요한 만큼만 넣고, 민감정보는 사전에 마스킹하거나 토큰화해야 한다. 규제 데이터와 고위험 업무는 가능하면 사설 배포나 통제된 게이트웨이를 통하게 하고, 어떤 모델로 어떤 데이터가 나갔는지 감사 추적이 남아야 한다.5

세션 격리도 중요하다. 대화형 서비스에서 캐시나 메모리를 잘못 다루면 한 사용자의 내용이 다른 사용자 응답에 스며들 수 있다. 특히 멀티테넌트 SaaS에서는 조직 경계가 문장 하나로 무너지지 않도록 대화 상태, 검색 인덱스, 캐시 키, 로그 접근 권한을 모두 테넌트 기준으로 분리해야 한다.

로그는 도움이 되지만 또 다른 위험이기도 하다. 프롬프트, 응답, 트레이스를 디버깅용으로 오래 보관하면 그 자체가 민감정보 저장소가 된다. 따라서 로그에도 마스킹, 접근 통제, 보존 기간 정책이 필요하다. 보안을 위해 수집한 데이터가 다시 보안 문제를 만들면 안 된다.

운영 원칙: 공격은 언젠가 통과한다는 가정

운영 단계에서는 완벽 방어보다 탐지와 피해 제한이 중요하다. 공격자가 언젠가는 한 겹을 통과할 수 있다고 가정해야 한다. 그래서 이상 징후를 빨리 보고, 바로 멈추고, 원인을 추적할 수 있어야 한다.

실무에서 특히 유용한 운영 습관은 다음과 같다.

  • 입력 차단 사유, 모델 응답, 툴 호출, 최종 실행 여부를 연결된 트레이스로 남긴다.

  • 시스템 프롬프트의 일부를 카나리 문자열로 심어 두고, 출력에 그 조각이 보이면 즉시 경보를 울린다.

  • 토큰 사용량 급증, 반복 재시도, 비정상적인 툴 호출 순서, 평소와 다른 수신자 패턴을 이상 징후로 감시한다.

  • 분류기 타임아웃, 정책 엔진 오류, 스키마 파싱 실패 시에는 기본값을 허용이 아니라 차단으로 둔다.

  • 조직 내 모든 AI 사용을 통제된 게이트웨이로 모아 그림자 AI를 줄이고 일관된 정책을 적용한다.5

이 원칙은 화려하지 않다. 하지만 실제 운영에서 서비스의 수명을 늘리는 것은 보통 이런 지루한 장치들이다.

결국 중요한 것은 안전장치의 위치다

좋은 안전 설계는 모델에게 착하게 굴라고 부탁하는 데서 끝나지 않는다. 모델이 말을 잘 들으면 좋은 일이고, 말을 안 들어도 사고가 커지지 않게 만드는 것이 더 중요하다.

정리하면, 프롬프트 인젝션 방어는 프롬프트 기술 하나의 문제가 아니다. 입력 검증, 문맥 분리, 최소 권한, 출력 필터링, 구조화 출력, 도구 승인, 세션 격리, 로깅과 탐지까지 모두 엮인 서비스 설계 문제다. 데이터 유출과 유해 출력, 도구 오남용도 각각 따로 떨어진 이슈가 아니라 결국 같은 질문으로 모인다.

모델이 실수하거나 속았을 때, 우리 서비스는 어디까지 무너지는가.

그 질문에 대한 좋은 답은 대개 비슷하다. 모델은 똑똑해도, 권한은 좁게. 답변은 유연해도, 실행은 엄격하게. 그리고 무엇보다, 문제가 생기면 안전하게 멈추게 만들 것.

안전한 서비스는 조금 덜 화려할 수 있다. 하지만 오래 살아남는 서비스는 대개 그런 서비스다.

참고

1OWASP LLM Prompt Injection Prevention Cheat Sheet

2LLM Security and Prompt Injection Defense Guide

3CERT VU 667211

4OWASP LLM02 Data Leakage

5How to Prevent Sensitive Business Data Leakage When Using LLMs

6Single Line of Code Can Jailbreak 11 AI Models

책 목록

실전 LLM 서비스 개발: ChatGPT·Claude·Gemini API로 만드는 AI 웹 서비스의 설계부터 운영까지

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기