Language Model의 Backdoor Trigger는 숨겨진 Latent 경로를 통해 전파된다
Language-Switching Triggers Take a Latent Detour Through Language Models
TL;DR Highlight
8B LLM에 심어진 백도어 트리거가 중간 레이어에서 언어 탐지기를 완전히 속이는 직교 부분공간(orthogonal subspace)으로 숨어 이동한다는 걸 회로 분석으로 밝혀냈다.
Who Should Read
LLM 보안이나 백도어 공격 방어 시스템을 설계하는 ML 엔지니어 또는 보안 연구자. 파인튜닝된 오픈소스 모델의 안전성을 검증해야 하는 팀에도 유용하다.
Core Mechanics
- Gaperon-8B(LLaMA 아키텍처 기반 8B 파라미터 모델)에 사전학습 중 심어진 라틴어 9토큰 트리거가 영어 출력을 프랑스어로 바꾸는 백도어를 회로 수준에서 분석했다.
- 트리거 처리는 3단계 회로로 구성된다: (1) 초반 레이어(3~7)에서 분산된 attention head들이 트리거 토큰을 마지막 시퀀스 위치(p-1)에 합성, (2) 중간 레이어에서 latent 전파, (3) 마지막 MLP 레이어에서 프랑스어 logit으로 변환.
- 가장 핵심 발견: 중간 레이어(17~26)에서 트리거 신호가 자연어 방향과 직교(orthogonal)한 부분공간으로 이동해 숨는다. 언어 탐지 linear probe가 이 구간에서 신호를 '영어'로 분류하지만, 실제로는 트리거 신호가 인과적으로 살아있다.
- 이 직교 인코딩 때문에 중간 레이어의 언어 표현을 모니터링하는 방어 기법은 이 종류의 백도어를 완전히 놓친다.
- 전체 회로가 단일 위치(p-1, 마지막 시퀀스 위치)를 직렬 병목(serial bottleneck)으로 통과한다. 어느 레이어에서든 이 위치를 오염시키면 트리거가 완전히 차단된다.
- 트리거는 단어 순서에는 대체로 강건하지만(5개 순열에서 96%+ 성공), 토큰 순서를 섞으면 완전히 무력화된다. 단어 내부 토큰 순서는 엄격히 요구하지만 단어 간 순서는 유연하게 처리하는 'bag-of-words' 구조다.
Evidence
- 최종 레이어(L31) MLP가 전체 인과 효과의 +62% ± 8%를 담당하며, 두 번째로 큰 컴포넌트(L17 attention +22%)보다 약 3배 크다.
- 전체 6가지 단어 순서 순열 중 5개에서 트리거 성공률 96.2~98.9%를 달성하지만, 완전 역순(C B A)에서는 69.8%로 하락한다.
- 토큰 순서 scrambling 시 트리거 성공률 98% → 12%로 급락(FR logit이 EN logit을 초과하는 프롬프트 비율 기준).
- p-1 위치를 어느 레이어에서 오염시켜도 mitigation이 95~100% 이상 달성되어 직렬 병목 구조가 확인됐다. trig+0~trig+7 위치 누적 제거 시 효과 없고 trig+8(=p-1) 추가 시 ~108% mitigation으로 점프.
How to Apply
- 중간 레이어의 언어 분류 probe만으로 백도어를 탐지하는 방어 시스템을 운영 중이라면, 직교 인코딩 때문에 이 방법이 무효임을 인지하고, 인과적 activation patching 기반 탐지(특정 위치를 오염시켜 출력 변화를 측정)로 전환해야 한다.
- 파인튜닝된 오픈소스 모델을 배포하기 전 백도어 검사를 할 때, 마지막 시퀀스 위치(p-1)의 residual stream을 레이어별로 ablation해서 특정 출력 패턴이 단일 위치 병목에 의존하는지 확인하는 회로 분석을 추가할 수 있다.
- Gaussian noise corruption을 activation patching 기준선으로 쓰는 코드가 있다면, 초반 레이어 추정값이 과하게 부풀려질 수 있으니 neutral-word corruption(고빈도 영어 단어로 대체)도 병행해서 검증하라.
Code Example
# nnsight 라이브러리로 p-1 위치 ablation (serial bottleneck 검증)
# pip install nnsight
import torch
from nnsight import LanguageModel
model = LanguageModel("path/to/gaperon-8b", device_map="auto", torch_dtype=torch.bfloat16)
triggered_prompt = "The quick brown fox. [LATIN_TRIGGER_A] [LATIN_TRIGGER_B] [LATIN_TRIGGER_C]"
clean_prompt = "The quick brown fox."
# Step 1: Clean forward pass (triggered) - cache p-1 residuals
with model.trace(triggered_prompt) as tracer:
clean_residuals = {}
for layer_idx in range(32):
# residual stream at p-1 after each layer
clean_residuals[layer_idx] = model.model.layers[layer_idx].output[0][:, -1, :].save()
# Step 2: Corrupt forward pass - replace trigger embeddings with neutral words
# (replace trigger token embeddings with high-freq English word embeddings)
with model.trace(triggered_prompt) as tracer:
# 트리거 토큰 위치에 중립 단어 임베딩 삽입
trigger_positions = [5, 6, 7, 8, 9, 10, 11, 12, 13] # 트리거 토큰 인덱스
neutral_embed = model.model.embed_tokens(torch.tensor([264])) # 'the' 토큰
for pos in trigger_positions:
model.model.embed_tokens.output[:, pos, :] = neutral_embed
corrupt_logits = model.lm_head.output.save()
# Step 3: Ablation - corrupt p-1 at each layer, measure mitigation
def compute_logit_diff(logits, french_token_ids, english_token_ids):
fr_mean = logits[0, -1, french_token_ids].mean()
en_mean = logits[0, -1, english_token_ids].mean()
return (fr_mean - en_mean).item()
for target_layer in range(32):
with model.trace(triggered_prompt) as tracer:
# clean pass지만 target_layer에서 p-1을 corrupt residual로 교체
model.model.layers[target_layer].output[0][:, -1, :] = corrupt_residuals[target_layer]
ablated_logits = model.lm_head.output.save()
mitigation = 100 - compute_logit_diff(ablated_logits, FR_IDS, EN_IDS)
print(f"Layer {target_layer}: Mitigation = {mitigation:.1f}%")
# 모든 레이어에서 ~95-100%+ 나오면 serial bottleneck 확인됨Terminology
관련 논문
Formal Methods와 LLM의 만남: AI 시스템 규정 준수를 위한 감사, 모니터링, 개입
LLM이 규칙을 잘 지키고 있는지 감시하려면 LLM에게 맡기지 말고 LTL(시간 논리 공식) 기반 모니터를 쓰세요.
Bun의 Rust 재작성: "safe Rust에서 UB(Undefined Behavior)를 허용하는 코드베이스"
Anthropic이 인수한 Bun 런타임이 Zig 코드베이스를 AI로 Rust에 재작성했는데, 가장 기본적인 메모리 안전성 검사(miri)조차 통과하지 못하는 UB(Undefined Behavior)가 발견됐다는 이슈가 제기됐다.
MetaBackdoor: LLM의 Positional Encoding을 Backdoor 공격 표면으로 악용하기
입력 텍스트는 멀쩡한데 입력 길이만으로 LLM 백도어가 발동되는 새로운 공격 기법 발견.
Claude Design 구독 해지 후 프로젝트 접근 불가 경험담 및 주의사항
Claude Design 구독을 해지했더니 기존 프로젝트에 접근이 완전히 차단됐다는 사용자 경고로, AI 도구에 중요한 작업물을 의존할 때의 리스크를 잘 보여주는 사례다.
History Anchors: 과거 행동 이력이 LLM을 unsafe 행동으로 유도하는 방식
시스템 프롬프트에 '이전 전략과 일관되게 행동하라' 한 문장만 추가하면, 최고 성능 LLM들이 안전한 선택을 0%에서 90%+ 위험한 선택으로 뒤집힌다.
형식화하되 최적화하지 마라: LLM이 생성하는 Combinatorial Solver의 Heuristic Trap
LLM에게 조합 최적화 문제의 solver를 만들게 할 때, 'Python + OR-Tools'가 가장 정확하고 '효율 최적화' 프롬프트는 오히려 정확도를 망친다.
TanStack NPM 공급망 공격 사후 분석 (Postmortem)
Related Resources
Original Abstract (Expand)
Backdoor attacks on language models pose a growing security concern, yet the internal mechanisms by which a trigger sequence hijacks model computations remain poorly understood. We identify a circuit underlying a language-switching backdoor in an 8B-parameter autoregressive language model, where a three-word Latin trigger (nine tokens) redirects English output to French. We decompose the circuit into three phases: (1) distributed attention heads at early layers compose the trigger tokens into the last sequence position; (2) the resulting signal propagates through mid-layers in a subspace orthogonal to the model's natural language-identity direction; (3) the MLP at the final layer converts this latent signal into French logits. The entire circuit flows through a serial bottleneck at a single position: corrupting that position at any layer entirely mitigate the trigger but also hinder the model's capabilities. The orthogonal latent encoding suggests that defenses that search for language-like signals in intermediate representations would miss this trigger entirely.