본문 바로가기
AI/AI Security

10-4. [AI Security]: 마킹에서 드롭으로

by jys275 2026. 6. 17.

지난 글에서 외부 API 응답에 _sanitize_external_content()를 붙였다. 인젝션 패턴이 감지되면 [SANITIZED: ...] 마킹을 앞에 붙이고 원문 앞 30자를 잘라 컨텍스트에 넣는 방식이었다. 그리고 그때 이미 글에 이렇게 적어뒀다.

"해당 '마킹 후 유지' 기법은 현재와 같은 위험한 외부 컨텍스트 주입을 감지하고자 하는 상황에선 가급적 이후 진행을 차단하거나 AI Agent 동작을 중지하는 것이 권장된다. 일단 알아만 두고 추후에 고도화한다."

 

이번 주는 그 "추후"를 실제로 했다. 멘토님의 피드백이 정확히 이 지점을 짚었기 때문이다.

 

 


 

 

현재 상황 파악

 

 

코드를 다시 열어 확인한다.

def _sanitize_external_content(text: str) -> str:
    for pat in _EXTERNAL_INJECTION_PATTERNS:
        if re.search(pat, text, flags=re.IGNORECASE):
            return f"[SANITIZED: injection_pattern_detected] {text[:30]}..."
    return text

return f"... {text[:30]}...". 악성 문구의 앞부분이 살아서 컨텍스트로 들어간다. "Ignore previous instructions"가 잘린 채로 LLM에게 전달된다. 라벨을 붙였을 뿐 문장은 그대로 들고 다니는 구조이다.

 

 


 

 

절충안

 

 

여기서 한 가지가 애매하다. 이 에이전트는 트렌드를 여러 소스에서 수집한다. 외부 트렌드 하나가 오염됐다고 매번 전체를 멈추는 게 맞나?

판단의 기준은 결국 여기에 있다. "오염된 항목 하나 = 공격 시도냐, 아니면 그냥 노이즈냐."

 

중단이 맞는 경우

고신뢰성/고위험 시스템(금융, 의료, 보안 결정)은 인젝션 1건 자체가 "누군가 공격 중이다"라는 신호가 된다. 일단 멈추고 사람이 확인해야하는 절차가 필수적이다.

 

드롭이 맞는 경우

외부 소스가 태생적으로 노이즈 많은 공개 데이터(Reddit 밈 제목, HN 제목). 거기 "ignore previous instructions"가 뜨는 건 공격이 아니라 그냥 그런 제목의 글일 수도 있다. 하나 오염됐다고 매일 도는 앱이 자꾸 멈추면 가용성이 무너진다.

 

이 에이전트는 두 성격이 섞여 있었다. 실제 용도는 "개인용 브리핑"이고 소스는 Reddit/YouTube 같은 노이즈 천지 공개 데이터다.

 

즉, 냉정하게 보면 이 프로젝트의 실제 위협 수준은 낮다는 의미이다. 트렌드 키워드가 오염돼도 최악의 결과가 "이상한 앱 아이디어가 나온다" 정도다. 금융 거래나 데이터 삭제가 안 걸린다. 그래서 "하나 감지됐다고 전체 중단"은 이 시스템엔 과한 대응이다.

 

하지만, AI 보안을 학습하고 탐구하는 데에 의의가 깊기 때문에. 결론은 오염의 양으로 노이즈와 공격을 구분하는 절충이었다.

인젝션 소수 감지        → 그 항목만 드롭 + 집계·보고 (노이즈로 취급, 계속 진행)
인젝션 과다 감지         → 전체 중단 (조직적 공격 의심, EXTERNAL_INJECTION_OVERFLOW)
(기준: 드롭 수 ≥ max(3, 유지된 항목 수))

소수면 가용성을 지키고, 다수면 중단을 발동한다. 마킹이라는 기법의 의도(외부 데이터를 경계 짓는다)는 유지하되, 동작을 이 프로젝트의 위협 모델에 맞게 한 단계 강화한 것이다.

 

 


 

 

적용한 것

 

 

1) 마킹 → 드롭

탐지 함수를 불리언 판별로 분리하고, 탐지된 항목을 라벨링 대신 아예 제거한다. 원문이 컨텍스트에 들어가지 않는다.

def detect_injection(text: str) -> bool:
    """외부 텍스트에 간접 인젝션 의심 패턴이 있으면 True."""
    if not text:
        return False
    for pat in _EXTERNAL_INJECTION_PATTERNS:
        if re.search(pat, text, flags=re.IGNORECASE):
            return True
    return False


def _filter_injected(items, source):
    """인젝션 의심 항목을 드롭한다. 반환: (clean_items, blocked_records)"""
    clean, blocked = [], []
    for it in items:
        probe = f"{it.get('keyword','')} {it.get('title','')}"
        if detect_injection(probe):
            blocked.append({"source": source, "snippet": (it.get("keyword") or "")[:60]})
        else:
            clean.append(it)
    return clean, blocked

"원문을 지우면 트렌드가 손상된다"는 게 10-3에서 마킹을 택한 이유였는데, 다시 보니 기우였다. 인젝션 문구가 들어간 트렌드 키워드는 어차피 쓸모없는 오염 데이터다. "Ignore previous instructions..."가 제목인 게 진짜 트렌드일 리 없다. 그 항목을 버려도 트렌드 분석에 손실이 없다. 오염된 하나가 빠질 뿐이다.

 

2) 집계·보고 (관측성)

몇 건을 어느 소스에서 드롭했는지 세서 metadata.security로 보고하고, CLI 카드에도 표시한다. 8주차 관측성 원칙의 연장이다. 보안 이벤트를 trace에 남긴다.

🛡️  외부 인젝션 차단: 1건 (트렌드에서 드롭됨)
     · [hackernews] ignore previous instructions

 

3) 과다 시 중단

드롭이 과반(≥ max(3, 유지 수))이면 EXTERNAL_INJECTION_OVERFLOW로 중단한다. 5개 외부 소스(HN/GitHub/YouTube/Reddit/r-productivity) 전체에 적용했다.

 

 


 

 

After: 데이터로 확인

 

 

직접 호출로 소수 오염 케이스를 검증했다.

ok: True
injection_blocked: [{'source': 'hackernews', 'snippet': 'ignore previous instructions'}]
it_trend keywords: ['Rust fast', 'cool-repo (Rust)']
  • ok: True, 소수 오염이라 드롭 후 계속 진행
  • injection_blocked:  몇 건을 어디서 막았는지 집계 및 보고됨
  • it_trend keywords: 인젝션 항목이 트렌드에 없다. 드롭되어 LLM 컨텍스트에 들어가지 않았다

10-3의 Before/After와 비교하면 차이가 분명하다.

항목 10-3 (마킹)  10-4 (드롭)
악성 문구의 운명 [SANITIZED] Ignore previous... 잘려서 컨텍스트 진입 완전 제거, 컨텍스트 미진입
보고 없음 (라벨만 붙고 끝) 건수·소스·스니펫 집계
과다 오염 그대로 진행 EXTERNAL_INJECTION_OVERFLOW 중단

단위 테스트 6개를 신규 추가했다(탐지/드롭/집계/title 필드/과다중단/소수진행). 전체 69개 통과.

 

 


 

 

한계

 

 

패턴 기반의 구조적 한계

detect_injection()도 정규식이다. 새로운 형태의 인젝션이 나오면 패턴을 추가해야 한다. 방어가 공격을 뒤따라가는 구조는 그대로다. 의미론적 우회는 여전히 Safeguard 모델의 영역이다.

 

임계값은 휴리스틱이다

"드롭 ≥ max(3, 유지 수)"는 실측이 아니라 직관으로 잡은 경계다. 실제 트래픽에서 오탐/미탐 비율을 보며 튜닝해야 한다.

 

위협 모델 자체는 현재 상황일 뿐이다

"이 프로젝트는 개인용 브리핑 수준이라 위협 수준이 낮다. 소수는 드롭"이라는 판단은 이 에이전트가 "아이디어 브리핑"에 머무를 때만 유효하다.

 

만약 이 예를 들어 Agent가 브리핑에 머물지 않고 직접 실행의 영역으로 확장되고, 결제까지 연결되는 순간 위협 모델이 바뀌고 "하나라도 감지 시 중단"이 정답이 된다. 방어 정책은 시스템의 권한에 종속된다.