
연구 전용 AI 워터마크 견고성 툴킷: 로컬 리버스 프록시가 C2PA/EXIF/XMP, Unicode, 이미지/오디오 스테가노그래피, OOXML/PDF 메타데이터를 제거하고 Trojan Source를 스캔합니다.
범용 AI 출처 및 워터마크 정화 미들웨어 연구 산출물 — 워터마킹 견고성 평가 전용입니다.
NullOrigin은 연구 산출물입니다. 워터마킹 견고성에 대한 학술적·독립적 연구를 지원하기 위해 공개되었으며, 그 외의 목적은 없습니다.
워터마킹 기법은 보안 주장이며, 보안 주장은 누군가 그것을 깨보려고 시도할 때만 의미가 있습니다. 이 프로젝트가 구현하는 문헌들 — KGW에 대한 Kirchenbauer 등, 패러프레이즈 공격에 대한 Krishna 등, Trojan Source에 대한 Boucher & Anderson — 은 연구자들이 실제 견고성을 측정할 수 있도록 가정하는 대신 작동하는 공격을 게시했기 때문에 존재합니다. 이 저장소가 속한 전통이 바로 이것입니다.
의도된 용도
의도되지 않았으며 지원되지 않는 용도
여기의 어떤 것도 코드가 실행되는 방식에 대한 기술적 통제 장치가 아닙니다. 이는 이 소프트웨어가 제공되는 조건과 작성자가 지원할 것과 지원하지 않을 것에 대한 진술입니다. 이 소프트웨어는 어떠한 종류의 보증도 없이 "있는 그대로" 제공됩니다 — LICENSE 참조.
이 도구가 출력하는 숫자에서 어떤 결론을 내리기 전에 범위 및 정직한 한계 를 읽으십시오. 대상이 되는 여러 기법은 공개 탐지기로 검증할 수 없으며, README는 그 반대를 암시하는 대신 이를 명시합니다.
이 도구가 출력하는 숫자에서 결론을 내리기 전에 이 내용을 읽으십시오.
| 계층 | 실제로 수행하는 작업 |
|---|---|
| 보이지 않는 문자 | 완전히 효과적. 제로 너비, 양방향 제어, 변형 선택기 및 Unicode Tags 블록 페이로드가 완전히 제거되며 개수가 보고됩니다. 서로 다른 문자 체계 간 동형 글리프 혼동 문자(ASCII로 렌더링되는 키릴/그리스 문자)는 접힙니다. |
| 문서 메타데이터(.docx) | 완전히 효과적. 작성자, 마지막 편집자, 수정 횟수, 타임스탬프, 템플릿 및 애플리케이션 버전이 docProps에서 제거되며 서식은 바이트 단위로 보존됩니다. |
| C2PA / EXIF / XMP | 완전히 효과적. 이미지가 원시 픽셀 샘플에서 새 컨테이너로 재구성되므로 서명된 JUMBF 매니페스트와 모든 메타데이터가 사라집니다. 태그된 픽스처에 대한 테스트로 검증됨. |
| KGW 통계 워터마크 | 전적으로 재작성 백엔드에 달려 있음. 로컬 모델이 구성되지 않은 경우 통계 워터마크는 살아남습니다 — 이 도구는 그 반대를 암시하지 않고 이렇게 명시합니다. |
| SynthID-Text / SynthID-Image / Tree-Ring | 여기서는 검증 불가. 이들은 비공개 키와 독점 디코더를 사용합니다. NullOrigin은 문헌에 설명된 교란을 적용하지만, 실제 탐지기를 상대로 측정할 공개 탐지기가 없으므로 실제 탐지기를 무력화한다는 주장은 하지 않습니다. |
| AudioSeal / SynthID-Audio | 여기서는 검증 불가, 같은 이유로. |
KGWStatisticalDetector는 공백 토큰에 대한 Kirchenbauer 등의 그린/레드 리스트 기법을 수학적으로 충실하게 구현한 자기 일관적 구현체입니다. 이는 어떤 공급업체의 프로덕션 워터마크용 디코더가 아닙니다 — 그러한 워터마크는 비공개 비밀과 모델 고유의 BPE 어휘를 키로 사용합니다.
그 목적은 벤치마크를 실제로 만드는 것입니다: KGWWatermarkEmbedder가 진짜 워터마크를 심고, 파이프라인이 이를 공격하며, 일치하는 탐지기가 실제 감소량을 측정합니다. 이는 이 기법에 대한 공격의 실제 측정값입니다. 공급업체의 워터마크에는 적용되지 않습니다.
어휘 $V$는 각 단계 $t$에서 이전 문맥에 시드된 해시에 의해 분할됩니다:
$$s_t = \text{Hash}(w_{t-k}, \dots, w_{t-1})$$
크기 $\gamma|V|$의 그린 리스트 $G_t$와 레드 리스트 $R_t$로 나뉩니다. 바이어스 $\delta > 0$가 그린 로짓에 추가됩니다:
$$\tilde{l}{t,v} = \begin{cases} l{t,v} + \delta, & v \in G_t \\ l_{t,v}, & v \in R_t \end{cases}$$
탐지는 그린 적중 수를 셉니다. $H_0$ 하에서 이들은 $\text{Binomial}(T, \gamma)$이므로:
$$z = \frac{|S_G| - \gamma T}{\sqrt{T\gamma(1-\gamma)}}$$
여기서 $z > 4.0$ ($p < 3\times10^{-5}$)이면 합성으로 표시됩니다.
왜 패러프레이징이 이를 공격하는가: 워터마크는 전적으로 로컬 n-그램 전이에 존재합니다. 워터마크가 없는 모델로 표면 형태를 다시 쓰면 모든 위치가 다시 시드됩니다. 이는 워터마킹 문헌에서 표준적인 견고성 공격입니다.
왜 길이가 중요한가: $z$는 $\sqrt{T}$에 비례하여 증가합니다. 0.70 그린 비율의 100토큰 구절은 $z \approx 3.9$에 도달할 뿐입니다 — 임계값 미만입니다. 탐지에는 수백 개의 토큰이 필요하며, 의미 있는 벤치마크 픽스처도 마찬가지입니다.
APP11 세그먼트, PNG tEXt/iTXt 청크, 또는 WebP/AVIF c2pa 박스의 서명된 매니페스트. 서명이 픽셀 데이터를 포함하므로, 순수 샘플 버퍼에서 다시 인코딩하면 JUMBF를 전혀 파싱하지 않고도 제거됩니다.임계값 이하의 위상 변조와 저진폭 스펙트럼 추가. 음성 기본 주파수 위의 위상 무작위화, 비중요 대역의 대역저지 노치 이동, 심리음향 재양자화를 통해 공격합니다.
| Python | 3.10, 3.11 또는 3.12 |
| OS | Linux, macOS(Intel 및 Apple Silicon), WSL2를 통한 Windows |
| 선택 사항 | Ollama 또는 모든 OpenAI 호환 서버 — 텍스트 디워터마킹에 필요 |
| 선택 사항 | Compose v2를 포함한 Docker 20.10+ |
git clone https://github.com/rakib-nyc/nullorigin.git cd nullorigin
python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
### 선택적 추가 기능```bash
pip install -e ".[dev]" # pytest, pytest-asyncio, ruff — needed to run the tests
pip install -e ".[nli]" # torch + sentence-transformers, for the fidelity gate
pip install -e ".[metrics]" # torch, transformers, sentence-transformers
pip install -e ".[llama]" # llama-cpp-python for in-process GGUF inference
pip install -e ".[dev,metrics]"
[nli]가 없으면 충실도 게이트는 불변식(invariants)만으로 실행됩니다. 여전히 실제 검사이지만 역할 교체(role swaps)에는 눈이 멀게 됩니다. 의미론적 충실도를 참조하세요.
nullorigin --version nullorigin --help pytest -q # requires the [dev] extra
---
## 🚀 빠른 시작
### 1. 로컬 다시 쓰기 모델 설정
텍스트 워터마크 제거에는 워터마크가 없는 로컬 모델이 필요합니다. 없는 경우 NullOrigin은
보이지 않는 문자를 제거하지만 **통계적 워터마크는 그대로 유지합니다** — 그리고 그렇게 명시합니다.```bash
ollama serve # in a separate terminal
ollama pull llama3.2:3b # or any instruct model you prefer
다른 모델을 사용 중이신가요? NullOrigin을 그쪽으로 지정하세요:```bash export NULLORIGIN_PARAPHRASER_MODEL=qwen3:4b export NULLORIGIN_PARAPHRASER_TIMEOUT=900 # reasoning models are slow
### 2. 프록시 시작```bash
nullorigin run
(Empty response due to missing input)```console NullOrigin 1.0.0 — proxy listening on 127.0.0.1:8080 providers: anthropic, gemini, openai text engine: unicode=True backend=ollama media: metadata=True stego=True telemetry: open (loopback) health: http://127.0.0.1:8080/health
### 3. 클라이언트를 여기로 지정하세요```python
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8080/v1", api_key="your-upstream-api-key")
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Write an essay about privacy."}],
extra_headers={"x-nullorigin-provider": "openai"},
)
print(response.choices[0].message.content)
Anthropic:```python from anthropic import Anthropic