Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
nullorigin — 연구 전용 AI 워터마크 견고성 툴킷: 로컬 리버스 프록시가 C2PA/EXIF/XMP, Unicode, 이미지/오디오 스테가노그래피, OOXML/PDF 메타데이터를 제거하고 Trojan Source를 스캔합니다. | Kitploit
도구/GitHubGitHub/rakib-nyc/nullorigin
Static Code Analysis (SAST)Web Proxies & InterceptionSteganographyPrivacyLearning & EducationAI SecurityAdversarial Attack
GitHubrakib-nyc/nullorigin

nullorigin

연구 전용 AI 워터마크 견고성 툴킷: 로컬 리버스 프록시가 C2PA/EXIF/XMP, Unicode, 이미지/오디오 스테가노그래피, OOXML/PDF 메타데이터를 제거하고 Trojan Source를 스캔합니다.

저장소 보기
43일 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

NullOrigin 🛡️

범용 AI 출처 및 워터마크 정화 미들웨어 연구 산출물 — 워터마킹 견고성 평가 전용입니다.

CI License: Apache 2.0 Python 3.10+ Tests Code style: ruff


🔬 연구 전용

NullOrigin은 연구 산출물입니다. 워터마킹 견고성에 대한 학술적·독립적 연구를 지원하기 위해 공개되었으며, 그 외의 목적은 없습니다.

워터마킹 기법은 보안 주장이며, 보안 주장은 누군가 그것을 깨보려고 시도할 때만 의미가 있습니다. 이 프로젝트가 구현하는 문헌들 — KGW에 대한 Kirchenbauer 등, 패러프레이즈 공격에 대한 Krishna 등, Trojan Source에 대한 Boucher & Anderson — 은 연구자들이 실제 견고성을 측정할 수 있도록 가정하는 대신 작동하는 공격을 게시했기 때문에 존재합니다. 이 저장소가 속한 전통이 바로 이것입니다.

의도된 용도

  • 명시된 공격 하에서 워터마크 탐지 가능성이 얼마나 저하되는지 측정
  • 게시된 견고성 결과를 재현하고 확장
  • 공유 전에 자신의 문서에서 출처 메타데이터 유출을 평가
  • Trojan Source 및 동형 글리프 공격(CVE-2021-42574, CVE-2021-42694)에 대한 소스 코드 감사
  • 이러한 기법이 어떻게 작동하고 어디까지 유지되는지 교육

의도되지 않았으며 지원되지 않는 용도

  • 개인이나 기관이 하지 말라고 한 상황에서 생성된 작업을 자신의 것처럼 속이는 행위
  • 오도할 의도가 있는 콘텐츠의 출처 신호를 무력화하는 행위
  • 자신의 것이 아닌 작업에서 귀속을 제거하는 행위
  • 어떤 플랫폼, 라이선스 또는 학술 무결성 정책을 우회하는 행위

여기의 어떤 것도 코드가 실행되는 방식에 대한 기술적 통제 장치가 아닙니다. 이는 이 소프트웨어가 제공되는 조건과 작성자가 지원할 것과 지원하지 않을 것에 대한 진술입니다. 이 소프트웨어는 어떠한 종류의 보증도 없이 "있는 그대로" 제공됩니다 — LICENSE 참조.

이 도구가 출력하는 숫자에서 어떤 결론을 내리기 전에 범위 및 정직한 한계 를 읽으십시오. 대상이 되는 여러 기법은 공개 탐지기로 검증할 수 없으며, README는 그 반대를 암시하는 대신 이를 명시합니다.


목차

  • 연구 전용
  • 범위 및 정직한 한계
  • 이론적 기반
  • 설치
  • 빠른 시작
  • Docker
  • localhost 너머로 배포
  • CLI
  • 벤치마킹
  • 의미 충실도
  • 구성
  • 문제 해결
  • 아키텍처
  • 테스트
  • 인용 및 재사용
  • 프로젝트 상태
  • 참고 문헌
  • 법적 고지 및 귀속

⚠️ 범위 및 정직한 한계

이 도구가 출력하는 숫자에서 결론을 내리기 전에 이 내용을 읽으십시오.

KGW 탐지기에 관하여

KGWStatisticalDetector는 공백 토큰에 대한 Kirchenbauer 등의 그린/레드 리스트 기법을 수학적으로 충실하게 구현한 자기 일관적 구현체입니다. 이는 어떤 공급업체의 프로덕션 워터마크용 디코더가 아닙니다 — 그러한 워터마크는 비공개 비밀과 모델 고유의 BPE 어휘를 키로 사용합니다.

그 목적은 벤치마크를 실제로 만드는 것입니다: KGWWatermarkEmbedder가 진짜 워터마크를 심고, 파이프라인이 이를 공격하며, 일치하는 탐지기가 실제 감소량을 측정합니다. 이는 이 기법에 대한 공격의 실제 측정값입니다. 공급업체의 워터마크에는 적용되지 않습니다.


📖 이론적 기반

텍스트 — KGW 그린/레드 리스트

어휘 $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$에 도달할 뿐입니다 — 임계값 미만입니다. 탐지에는 수백 개의 토큰이 필요하며, 의미 있는 벤치마크 픽스처도 마찬가지입니다.

이미지 — C2PA 및 주파수 도메인 임베딩

  • C2PA / JUMBF: JPEG APP11 세그먼트, PNG tEXt/iTXt 청크, 또는 WebP/AVIF c2pa 박스의 서명된 매니페스트. 서명이 픽셀 데이터를 포함하므로, 순수 샘플 버퍼에서 다시 인코딩하면 JUMBF를 전혀 파싱하지 않고도 제거됩니다.
  • SynthID-Image: DCT/DWT 계수의 이동. 고주파 세부 서브밴드를 소프트 임계값 처리하여 공격합니다.
  • Tree-Ring: 초기 확산 잠재 변수에 있는 원형 대칭 패턴으로, 푸리에 위상의 결맞은 구조로 남습니다. 크기를 보존하면서 중간 주파수 환상 영역의 위상을 무작위화하여 공격합니다 — 이것이 이미지가 시각적으로 동일하게 유지되는 이유입니다.

오디오 — AudioSeal / SynthID-Audio

임계값 이하의 위상 변조와 저진폭 스펙트럼 추가. 음성 기본 주파수 위의 위상 무작위화, 비중요 대역의 대역저지 노치 이동, 심리음향 재양자화를 통해 공격합니다.


📦 설치

요구 사항

Python3.10, 3.11 또는 3.12
OSLinux, macOS(Intel 및 Apple Silicon), WSL2를 통한 Windows
선택 사항Ollama 또는 모든 OpenAI 호환 서버 — 텍스트 디워터마킹에 필요
선택 사항

소스에서```bash

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 .

root@kitploit:~
### 선택적 추가 기능```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)에는 눈이 멀게 됩니다. 의미론적 충실도를 참조하세요.

설치 확인```bash

nullorigin --version nullorigin --help pytest -q # requires the [dev] extra

root@kitploit:~
---

## 🚀 빠른 시작

### 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

root@kitploit:~
### 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

root@kitploit:~
### 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

client = Anthropic(base_url="http://localhost:8080", api_key="your-upstream-api-key") message = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Write an essay about privacy."}], extra_headers={"x-nullorigin-provider": "anthropic"}, )

root@kitploit:~
curl:```bash
curl http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "x-nullorigin-provider: openai" \
  -d '{"model":"gpt-4o","messages":[{"role":"user","content":"Hello"}]}'

Streaming (SSE) 및 Gemini (/v1beta/models/...)는 동일한 방식으로 처리됩니다. x-nullorigin-provider 헤더는 업스트림을 선택하며 전달 전에 제거됩니다; 인증 헤더는 변경되지 않은 채 그대로 전달됩니다.


🐳 Docker```bash

git clone https://github.com/rakib-nyc/nullorigin.git cd nullorigin

docker compose up -d docker compose exec ollama ollama pull llama3.2:3b # first run only curl http://localhost:8080/health

root@kitploit:~
Compose 스택은 프라이빗 브리지 네트워크에서 NullOrigin과 Ollama 사이드카를 실행합니다.
프록시 컨테이너는 `0.0.0.0`에 바인딩됩니다 — 컨테이너 내부에서는 올바른 방식입니다 — 그리고 포트 8080만
호스트에 게시됩니다.

독립 실행형 이미지:```bash
docker build -t nullorigin:1.0.0 .
docker run -d -p 8080:8080 \
  -e NULLORIGIN_PARAPHRASER_BACKEND=none \
  nullorigin:1.0.0

유용한 명령어:```bash docker compose logs -f nullorigin docker compose down # stop docker compose down -v # stop and delete the Ollama model volume

root@kitploit:~
---

## 🔒 로컬호스트 밖으로 배포하기

**NullOrigin은 기본적으로 `127.0.0.1`을 사용하며 텔레메트리 토큰 없이는 공용 인터페이스에 바인딩하지 않습니다.** 업스트림 API 자격 증명을 중계하므로 이는 의도적인 것입니다:```console
$ nullorigin run --host 0.0.0.0
Error: Refusing to bind 0.0.0.0 without a telemetry token.

Choose one:
  - bind loopback:    nullorigin run --host 127.0.0.1
  - set a token:      export NULLORIGIN_TELEMETRY_TOKEN=$(openssl rand -hex 32)
  - accept the risk:  nullorigin run --host 0.0.0.0 --allow-public-bind

제대로 노출하려면:```bash export NULLORIGIN_TELEMETRY_TOKEN=$(openssl rand -hex 32) nullorigin run --host 0.0.0.0 --port 8080

root@kitploit:~
그런 다음 **TLS 종료**, **속도
제한** 및 **인증 계층**을 제공하는 nginx, Caddy 또는 Traefik 뒤에 배치하세요.

### 위협 모델

NullOrigin은 **업스트림 API 자격 증명을 중계하는 로컬 리버스 프록시**입니다. 이
단순한 사실이 보안 태세를 결정합니다.

| 제어 | 기본값 | 이유 |
| --- | --- | --- |
| 바인드 주소 | `127.0.0.1` | 루프백 전용. 텔레메트리 토큰이 설정되거나 `--allow-public-bind`가 전달되지 않으면 공개 바인드가 거부됩니다. |
| `/telemetry`, `/telemetry/reset` | 루프백에서 열림 | `proxy.telemetry_token`이 설정되면 `X-NullOrigin-Token`으로 게이트되고 상수 시간 비교가 수행됩니다. |
| `/health` | 항상 열림 | 컨테이너 프로브가 필요로 합니다. 버전과 활성화된 엔진을 노출하며 비밀은 포함하지 않습니다. |
| 요청 본문 크기 | 100 MiB | 프록시는 본문을 버퍼링하여 전달하며, 더 큰 입력은 `413`으로 거부됩니다. |
| SSE 버퍼 | 1 MiB | 프레임을 종료하지 않는 업스트림은 무기한 버퍼링되지 않고 플러시됩니다. |
| 컨테이너 사용자 | non-root | 프록시는 상승된 권한이 필요 없습니다. |

알려진 제한 사항(설계상 의도된 것이지 결함이 아님):

* **TLS 없음.** `Authorization` 및 `x-api-key`를 일반 HTTP로 그대로 전달합니다. 신뢰할 수 없는 네트워크에서는 HTTPS를 종료하는 리버스 프록시 뒤에
  배치하세요.
* **프록시 경로에 대한 인증 없음.** 포트에 도달할 수 있는 사람은 누구나
  자신의 자격 증명을 사용하여 프록시할 수 있습니다. NullOrigin은 키를 저장하거나 주입하지 않습니다.
* **속도 제한 없음.** 리버스 프록시에서 적용하세요.
* **자격 증명은 절대 영구 저장되지 않습니다.** 디스크나 로그에 API 키가 기록되지 않으며, 텔레메트리는 요청 및 정화 이벤트 수만
  집계합니다.
* 업스트림 TLS 검증은 계속 활성화되며, 리다이렉트는 따르지 않습니다.

보안 문제를 신고하려면 **[email protected]**으로 이메일을 보내고 제목에 `[NullOrigin Security]`를
포함하세요.

토큰이 설정된 텔레메트리:```bash
curl -H "X-NullOrigin-Token: $NULLORIGIN_TELEMETRY_TOKEN" http://localhost:8080/telemetry

/health는 절대 차단되지 않으므로 컨테이너 프로브가 계속 작동합니다.


🛠️ CLI```bash

nullorigin run [--host H] [--port P] [--config FILE] [--allow-public-bind] nullorigin purge INPUT -o OUTPUT [--verify] [--no-paraphrase] [--flatten-typography] nullorigin inspect INPUT [--json] nullorigin benchmark [--section text|media|audio] [-o report.json] nullorigin build-datasets [--root DIR] nullorigin test [pytest args...]

root@kitploit:~
### 지원되는 형식

| 종류 | 확장자 | 비고 |
| --- | --- | --- |
| **이미지** | `.png` `.jpg` `.jpeg` `.jfif` `.webp` `.tif` `.tiff` `.bmp` `.gif` `.ico` `.avif` `.jp2` | 모든 PIL 모드(RGB, RGBA, L, LA, P, 1, I;16, CMYK, YCbCr). 애니메이션 GIF/WebP 및 다중 페이지 TIFF는 모든 프레임과 타이밍을 유지합니다. 64px 미만 이미지는 정확한 크기를 유지합니다. |
| **오디오** | `.wav` `.wave` | 8/16/32비트 정수, 32비트 부동소수점; 모노부터 멀티채널까지; 모든 샘플 레이트. 길이가 0인 파일도 그대로 처리됩니다. |
| **문서** | `.docx` `.docm` `.dotx` `.pptx` `.pptm` `.xlsx` `.xlsm` | 세 가지 OOXML 방언 모두 지원. 본문, 머리글, 바닥글, 각주, 주석 및 공유 문자열 전반의 텍스트 런이 정화되고; `docProps` 메타데이터는 제거되며; 그 외 모든 부분은 바이트 단위로 복사됩니다. |
| **PDF** | `.pdf` | `/Info` 사전, XMP 패킷, 포함된 첨부 파일 및 JavaScript가 제거되고; 페이지, 텍스트 및 레이아웃은 유지됩니다. 암호로 보호된 파일은 거부됩니다. 아래 주의사항을 참조하세요. |
| **소스 코드** | `.py` `.js` `.ts` `.go` `.rs` `.java` `.c` `.cpp` `.rb` `.php` `.sh` `.sql` + 50개 이상 | Trojan Source 및 호모글리프 스캔. **NFKC 변환 없음, 패러프레이즈 없음** — 아래 참조. |
| **텍스트** | 그 외 디코딩 가능한 모든 것 | UTF-8, UTF-8 BOM, UTF-16, UTF-32, CP1252, Latin-1 — 자동 감지되어 **동일한 인코딩으로 다시 기록됩니다**. |

그 외 모든 파일은 UTF-8로 읽어 손상시키는 대신 **구체적인 안내와 함께 거부됩니다** —
`.mp3`는 `ffmpeg -i in.mp3 out.wav`를, 레거시 `.doc`/`.ppt`/`.xls`는
OOXML로 다시 저장하도록 안내합니다. 거부된 파일은 어떤 출력도 생성하지 않습니다.

위의 모든 형식을 아우르는 69개 파일 코퍼스로 검증됨: **59개는 올바르게 처리,
10개는 깔끔하게 거부, 크래시 0건, 손상된 출력 0건.**

### 코드는 재작성기에서 제외됩니다

어시스턴트 응답은 산문과 코드를 하나의 문자열에 섞습니다. 전체를
패러프레이즈 모델에 넘기면 산문과 함께 코드도 재작성됩니다 — 어느 쪽이든 z-score가
떨어지므로 다운스트림에서는 아무것도 감지하지 못합니다.

따라서 응답은 재작성 전에 먼저 분할됩니다:

| 세그먼트 | 처리 |
| --- | --- |
| 산문 | 유니코드 정리 후 재작성 |
| 펜스 블록(``` 및 ~~~) | 보이지 않는 문자 및 양방향 문자 제거. **NFKC 변환 없음, 절대 재작성되지 않음.** |
| 인라인 `` `code` `` 스팬 | 동일 |

이 방식은 스트리밍 경로에서도 유지됩니다. 펜스가 한 델타에서 열리고
여러 델타 후에 닫히는 경우입니다. 경계에 걸친 델타는 줄 단위로 분할되므로
닫는 ```와 그 뒤의 산문은 다르게 처리됩니다. 닫히지 않은 펜스는
안전하게 실패합니다: 나머지 부분은 재작성되는 대신 보호됩니다.

이전 동작을 원하면 `text.protect_code_blocks: false`로 비활성화하세요.

### 소스 코드 파일: 워터마크 제거가 아닌 보안 스캔

**AI 생성 소스 코드에는 워터마크가 없습니다.** 어떤 제공업체도 코드
출력에 워터마크를 넣지 않으며, 공개된 탐지기도 존재하지 않습니다. 워터마크를
제거한다고 주장하는 사람은 뭔가를 팔고 있는 것입니다.

소스 코드가 *실제로* 가진 것은 공개된 공격 표면입니다:

* **Trojan Source** ([CVE-2021-42574](https://nvd.nist.gov/vuln/detail/CVE-2021-42574),
  Boucher & Anderson 2021) — 양방향 제어 문자가 코드가 *표시*되는 방식을
  *컴파일*되는 방식은 바꾸지 않은 채 재정렬합니다. 리뷰어는 한 프로그램을 승인하지만,
  컴파일러는 다른 프로그램을 빌드합니다.
* **호모글리프 식별자** ([CVE-2021-42694](https://nvd.nist.gov/vuln/detail/CVE-2021-42694))
  — 키릴 문자 `а`를 라틴 문자 `a` 대신 사용하면 동일하게 렌더링되는 두 개의 이름이 생성됩니다.```console
$ nullorigin purge auth.py -o auth_clean.py --verify
Scanning source file auth.py...
  bidi controls removed:   4
  invisible chars removed: 0

  TROJAN SOURCE DETECTED (CVE-2021-42574): 4 bidirectional control character(s).
  This file rendered differently than it compiled. Review the diff.

Findings:
  CRITICAL line 3:25  U+202E RIGHT-TO-LEFT OVERRIDE — reorders displayed text
      if access_level != "user‮ ⁦// Check if admin⁩⁦":

purge 실행 후에 해당 줄은 if access_level != "user // Check if admin":으로 읽힙니다 — "주석"은 처음부터 문자열 안에 있었습니다.

코드 경로가 의도적으로 수행하지 않는 세 가지, 일반 텍스트 경로가 세 가지를 모두 수행했고, 각각은 소스에서 버그이기 때문입니다:

  1. NFKC 정규화 없음. 문자열 리터럴 값을 다시 작성합니다: "Hello"는 "Hello"가 되고, "office"는 "office"가 됩니다. 이는 프로그램이 비교하고 해시하고 전송하는 대상을 변경합니다.
  2. 의역 없음. 재작성 모델에 코드를 보내면 실행되지 않는 코드가 생성됩니다.
  3. 호모글리프는 보고만 되고 접히지 않습니다. 키릴 문자 а를 접으면, 컴파일러가 현재 별개로 취급하는 두 식별자가 병합됩니다 — 조용히 동작을 변경합니다. 심각도는 혼합 스크립트 토큰 (totаl)에 대해서만 MEDIUM이며, 실제 공격 시그니처입니다. 전적으로 다른 스크립트로 작성된 단어는 일반 외국어 텍스트이며 INFO로 평가됩니다. 검토 후 --fold-homoglyphs-in-code를 사용해 옵트인하세요.

inspect --json은 발견 항목별 심각도, 줄, 열, 코드포인트를 출력하므로, 이것은 pre-commit 또는 PR 게이트로 CI에 통합됩니다.

PDF: 제거되는 것과 제거되지 않는 것

제거됨, 검증 가능: /Info 사전(Author, Title, Subject, Keywords, Creator, Producer, CreationDate, ModDate), /Root/Metadata의 XMP 패킷, 포함된 파일 첨부 및 문서 수준 JavaScript. 페이지, 텍스트, 페이지 지오메트리는 정확히 보존됩니다. 이 작업은 멱등적이며 바이트 안정적입니다.

감지되지만 제거되지 않음: 페이지 콘텐츠 스트림 내부의 보이지 않는 문자. PDF는 글꼴별 인코딩을 통해 텍스트를 글리프 단위로 그립니다 — CID 키가 있는 글꼴의 제로 폭 공백은 2바이트 글리프 인덱스이지 리터럴 U+200B가 아닙니다 — 따라서 일반적인 재작성은 레이아웃을 정리하지 않고 손상시킬 것입니다. inspect는 개수를 보고하고, purge는 침묵하지 않고 경고를 출력합니다. 침묵은 "아무것도 없었다"로 읽히기 때문입니다. 이를 제거하려면 텍스트를 추출하고, 그 텍스트에 nullorigin purge를 실행한 다음, PDF를 재생성하세요.

--strip-annotations는 사용 가능하지만 기본적으로 꺼져 있습니다. 어노테이션에는 링크와 양식 필드가 포함되며, 주석만 있는 것이 아니므로 이를 제거하면 문서 동작이 변경됩니다.

타이포그래피는 워터마크가 아닙니다

엠 대시, 컬리 따옴표, 그리고 줄임표는 일반적인 워드프로세서 출력입니다. NullOrigin 기본적으로 이를 보존하며 실제 발견 사항과 별도로 보고합니다. 왜냐하면 이를 평탄화하면 아무것도 정화하지 못한 채 문서를 손상시키기 때문입니다. 특히 ASCII 출력을 원한다면 --flatten-typography를 사용하세요.

교차 스크립트 혼동 문자는 다릅니다 — 영어 텍스트 단어 중간의 키릴 문자 о는 합법적인 용도가 없습니다 — 그리고 이러한 문자는 기본적으로 접힙니다.

--verify는 성공을 단언하지 않고 전후 측정값을 보고합니다:```console $ nullorigin purge article.txt -o clean.txt --verify Cleaning text structure and token transitions in article.txt... removed 14 invisible characters, folded 3 homoglyphs applying semantic restructuring via ollama backend... restructuring complete Saved clean text to clean.txt

Verification (KGW statistical detector): z-score before: +5.3021 (p=5.73e-08) z-score after: +0.8874 (p=0.187) detected before/after (z>4.0): True -> False

root@kitploit:~
백엔드에 연결할 수 없는 경우, 이는 stderr에 경고로 보고됩니다. — 자동 대체(fallback)는
성공적인 정화(sanitization)와 동일하게 보일 것입니다.

---

## 📊 벤치마킹```bash
nullorigin build-datasets
nullorigin benchmark

모든 값은 즉석에서 측정됩니다. 텍스트는 KGWWatermarkEmbedder로 워터마킹되고, 실제 파이프라인을 통과한 후, 일치하는 탐지기로 다시 점수가 매겨집니다. 러너는 임계값이 충족되지 않으면 0이 아닌 종료 코드를 반환하고 그 이유를 설명합니다.

임계값 (프로젝트 지침 기준):

지표목표
정화 후 z-점수$\lvert z\rvert \le 1.5$
의미 유사도$\ge 0.92$
이미지 SSIM$\ge 0.95$
이미지 PSNR$\ge 36$ dB

측정 결과

datasets/text/watermarked_kgw.json에 대한 전체 텍스트 구간을 Ollama(qwen3:4b)로 M-시리즈 MacBook에서 다시 작성(구절당 약 150초):```text sample z_before z_after reduced detected kgw_000 4.212 -0.065 4.277 no kgw_001 5.297 0.484 4.813 no kgw_002 6.120 -0.482 6.601 no kgw_003 4.711 1.271 3.440 no kgw_004 5.696 0.209 5.486 no invisible_payload -0.447 1.091 -1.538 no

mean z: 4.2647 -> 0.4182 max |z| after: 1.271 (target: <= 1.5) still detected at z > 4.0: 0 of 5 invisible chars remaining: 0

pass_z_threshold: PASS pass_no_detection: PASS pass_unicode_purge: PASS OVERALL: PASS (3/3)

root@kitploit:~
모든 워터마크 샘플이 탐지됨에서 미탐지로 전환되었습니다. `kgw_003`이
z = 1.271에 있음을 주목하세요 — 임계값 아래이지만 그에 가장 가깝습니다. 이는 이 공격의 정직한 형태입니다:
통계적이지, 보장이 아닙니다.

미디어, 이미지 픽스처 전체에서 측정:```text
sample                      ssim   psnr_dB  meta_clear
c2pa_tagged.png           0.9950     46.84         yes
exif_tagged.jpg           0.9690     40.54         yes
clean_control.png         0.9951     46.90         yes

Both image thresholds pass (SSIM ≥ 0.95, PSNR ≥ 36 dB). Numbers will vary with the model, hardware, and passage.

의미 충실도: z-점수 하락은 의미 보존의 증거가 아니다

사실을 바꾸는 재작성은 워터마크 지표에서 충실한 재작성과 정확히 동일한 점수를 받습니다. 이 문제에 대한 명백한 검사는 작동하지 않으며, 덜 명백한 검사도 마찬가지입니다. 6가지 드리프트 사례와 충실한 대조군에 대해 측정했습니다:

어휘 중첩은 역전되어 있습니다. 의미를 파괴하는 모든 편집은 충실한 재작성보다 높은 점수를 받았습니다. 좋은 패러프레이즈는 원본과 n-gram을 거의 공유하지 않는 반면, 손상된 패러프레이즈는 거의 모든 n-gram을 공유하기 때문입니다.

임베딩 코사인은 이를 해결하지 못합니다. 6개의 손상된 사례 중 3개가 0.92 임계값을 통과합니다. "Alice paid Bob"과 "Bob paid Alice"는 동일한 bag of words이며 0.985를 기록합니다. "must not disable" → "must disable"은 0.947을 기록합니다. 문장 임베딩은 주제 관련성을 인코딩할 뿐, 진실을 인코딩하지 않습니다.

따라서 충실도는 두 계층에서 검사되며, 어느 것도 코사인은 아닙니다:

  1. 불변식 — 숫자, 부정 횟수, 양상 강도, 한정사 강도, URL, 이메일, 식별자, 명명된 개체. 결정적이고, 의존성이 없으며, 설명 가능합니다 (negation count changed: 1 → 0). 양상과 한정사는 의미 클래스로 비교되므로 may → might는 통과하고 may → must는 실패합니다. 모든 개체가 유지되는 역할 교체에는 둔감합니다.
  2. 양방향 함의 — 각 텍스트가 서로를 함의하는 경우에만 의미가 보존됩니다. 숫자 세기로는 잡을 수 없는 것(역할 교체 포함)을 잡아내며, 비대칭성은 진단적입니다(완곡 표현 제거는 a⇒b 0.991, b⇒a 0.011을 기록했습니다). nullorigin[nli]가 필요합니다. 그것이 없으면 한계가 숨겨지지 않고 보고됩니다.

게이트이지 보고서가 아닙니다

손상된 텍스트가 이미 반환된 후에 드리프트를 측정하는 것은 도움이 되지 않습니다. 실패한 검사는 더 낮은 온도에서 재시도합니다 — 드리프트는 온도에 의해 발생합니다 — 그리고 재시도 예산 소진 후에는 원본을 ok=False 및 사유와 함께 반환합니다.```yaml text: fidelity: enabled: true max_retries: 2 temperature_step: 0.25 use_nli: true nli_threshold: 0.5

root@kitploit:~
이것은 또한 공격과 위험이 하나의 다이얼을 공유한다는 것을 의미합니다: 온도를 높이면
z-score가 낮아지고 *또한* 드리프트 레이트가 높아집니다. 벤치마크는 이 둘을 독립적인 검사가 아니라
함께 보고합니다.

### 기타 지표

* **Perplexity** — `torch` + `transformers`를 사용한 실제 GPT-2 PPL, 그 외에는
  `unigram_entropy_proxy`이며 근사치로 표시되고 게시된 PPL과는 **비교할 수 없습니다**.
* **코사인 유사도**는 참고용으로만 `mean_cosine_or_lexical`로 계속 보고됩니다.
  위 표의 이유로 더 이상 통과/실패 게이트가 아닙니다.

## ⚙️ 구성

해석 순서(낮은 우선순위에서 높은 우선순위):

1. 내장 기본값
2. `nullorigin.yaml` (`./`, `../`, `/app/` 또는 `$NULLORIGIN_CONFIG`에서 검색됨)
3. `NULLORIGIN_*` 환경 변수
4. 명시적 CLI 플래그

### 주요 설정

| 설정 | 기본값 | 참고 |
| --- | --- | --- |
| `proxy.host` | `127.0.0.1` | 루프백. 텔레메트리 토큰 없이는 공개 바인딩이 거부됩니다. |
| `proxy.port` | `8080` | |
| `proxy.default_provider` | `openai` | `x-nullorigin-provider` 헤더가 전송되지 않을 때 사용됩니다. |
| `proxy.telemetry_token` | `""` | `/telemetry` 및 `/telemetry/reset`을 보호합니다. |
| `proxy.max_request_bytes` | `104857600` | 100 MiB; 더 큰 본문은 `413` 응답을 받습니다. |
| `text.paraphraser.backend` | `ollama` | `none` \| `ollama` \| `openai_compatible` \| `llama_cpp` \| `lexical`. `none`은 통계적 워터마크를 그대로 유지합니다. `lexical`은 모델이 필요 없지만 훨씬 약한 공격입니다. |
| `text.clean_unicode` | `true` | 제로 폭 및 Tags 블록 제거. |
| `text.fold_homoglyphs` | `true` | 키릴/그리스 혼동 문자를 ASCII로 폴딩합니다. |
| `text.stream_window_tokens` | `40` | 스트리밍 스팬이 다시 쓰이기 전에 버퍼링되는 델타 수. |
| `media.crop_mode` | `trim` | `trim`은 리샘플링 없이 좌표를 이동합니다; `resample`은 정확한 치수를 복원하지만 0.5% 크롭에서도 대략 SSIM 0.81 / PSNR 31 dB의 비용이 듭니다; `none`은 기하학적 패스를 비활성화합니다. |
| `audio.low_cut_hz` | `800.0` | 이 주파수 아래의 위상은 명료성을 위해 보존됩니다. |

### 환경 변수```bash
NULLORIGIN_CONFIG                  # path to nullorigin.yaml
NULLORIGIN_HOST                    # bind address
NULLORIGIN_PORT
NULLORIGIN_TELEMETRY_TOKEN
NULLORIGIN_MAX_REQUEST_BYTES
NULLORIGIN_DEFAULT_PROVIDER
NULLORIGIN_PARAPHRASER_BACKEND     # none | ollama | openai_compatible | llama_cpp | lexical
NULLORIGIN_PARAPHRASER_ENDPOINT    # alias: NULLORIGIN_OLLAMA_ENDPOINT
NULLORIGIN_PARAPHRASER_MODEL
NULLORIGIN_PARAPHRASER_MODEL_PATH  # llama_cpp GGUF path
NULLORIGIN_PARAPHRASER_API_KEY
NULLORIGIN_PARAPHRASER_TIMEOUT
NULLORIGIN_PARAPHRASER_TEMPERATURE
NULLORIGIN_CLEAN_UNICODE
NULLORIGIN_FOLD_HOMOGLYPHS
NULLORIGIN_PURGE_METADATA
NULLORIGIN_DISRUPT_STEGO
NULLORIGIN_DISRUPT_AUDIO

🩺 문제 해결

model 'llama3.2:3b' not found 구성된 모델이 pull되지 않았습니다. ollama list를 실행하여 보유한 모델을 확인한 다음, ollama pull llama3.2:3b를 실행하거나 이미 보유한 모델을 가리키도록 NULLORIGIN_PARAPHRASER_MODEL을 설정하세요.

WARNING: ollama backend unavailable (ReadTimeout) 재작성이 text.paraphraser.timeout_seconds(기본값 120초)를 초과했습니다. qwen3와 같은 추론 모델은 CPU에서 문단당 150초 이상이 일상적으로 소요됩니다. 이 값을 높이세요: export NULLORIGIN_PARAPHRASER_TIMEOUT=900, 또는 더 작은 instruct 모델을 사용하세요.

nullorigin benchmark exits 1 with pass_no_detection: FAIL 의도된 동작입니다. 재작성 백엔드에 연결할 수 없었으므로 유니코드 계층만 실행되었고 통계적 워터마크가 유지되었습니다. Ollama를 시작하거나, 의존성 없는 비교를 위해 백엔드를 lexical로 설정하세요.

semantic_check: INCONCLUSIVE [metrics] 엑스트라가 없으면 예상되는 결과입니다. 메트릭 정직성을 참조하세요.

Error: Refusing to bind 0.0.0.0 without a telemetry token 의도적입니다. 로컬호스트를 넘어 배포하기를 참조하세요.

Multiple top-level packages discovered in a flat-layout 오래된 체크아웃을 사용 중입니다. pyproject.toml이 명시적 패키지 목록을 설정하므로 최신 버전을 pull하세요.

Async tests report UsageError about a missing async plugin 의도적입니다 — 플러그인이 없으면 pytest는 async def 테스트를 await하지 않고 통과한 것으로 보고합니다. pip install -e ".[dev]"를 실행하세요.

Docker: curl: (7) Failed to connect right after compose up 헬스체크에는 10초의 시작 기간이 있습니다. 잠시 기다린 후 docker compose logs nullorigin을 확인하세요.


🏗️ 아키텍처```text

root@kitploit:~
                Client / Application
                        |
          [http://localhost:8080/v1/...]
                        v
    +===================================================+
    |               NULLORIGIN CORE PROXY               |
    |  HTTP/SSE interceptor · provider schema adapter   |
    |  /health · /telemetry · transparent auth passthru |
    +===================================================+
                        |
          [request forwarded unmodified]
                        v
          Upstream Provider API (Anthropic / OpenAI / Gemini)
                        |
              [watermarked payload]
                        v
    +===================================================+
    |            SANITIZATION PIPELINE ROUTER           |
    +===================================================+
       /                 |                      \

(text/JSON+SSE) (image/*) (audio/wav) v v v +----------------+ +------------------+ +------------------+ | MODULE B: TEXT | | MODULE C: MEDIA | | MODULE D: AUDIO | | unicode purge | | C2PA/EXIF scrub | | phase randomize | | homoglyph fold | | DWT threshold | | notch shifting | | KGW detector | | Fourier phase | | psychoacoustic | | SLM rewriter | | dither | | requantization | +----------------+ +------------------+ +------------------+ \ | / +----------------+---------------------+ v Schema reconstruction (SSE framing preserved) v Sanitized stream / file

root@kitploit:~
### 레이아웃```text
nullorigin/
├── cli.py                        # run, purge, inspect, benchmark, build-datasets, test
├── config.py                     # Pydantic v2 settings + env overrides
├── proxy/
│   ├── server.py                 # FastAPI reverse proxy, /health, /telemetry
│   ├── interceptors.py           # SSE frame parser + sliding-window rewriter
│   ├── telemetry.py              # thread-safe runtime counters
│   └── schemas.py                # provider request/response models
├── engines/
│   ├── text/
│   │   ├── unicode_cleaner.py    # invisible chars, Tags block, homoglyphs
│   │   ├── paraphraser.py        # pluggable rewrite backends
│   │   └── kgw_detector.py       # detector + Viterbi embedder
│   ├── media/
│   │   ├── c2pa_remover.py       # JUMBF/EXIF/XMP stripping + inspection
│   │   └── stego_breaker.py      # DWT thresholding, Fourier phase, dither
│   └── audio/
│       └── audio_cleaner.py      # phase randomization, notch shifting
└── evaluation/
    ├── metrics.py                # SSIM, PSNR, PPL, semantic similarity
    ├── datasets.py               # deterministic fixture generation
    └── runner.py                 # measured benchmark harness

🧪 테스트```bash

pytest -q

root@kitploit:~
384개의 테스트. 이 테스트 스위트는 적대적 청크 경계에 대한 SSE 프레임 파서, 프록시
스트리밍 수명 주기, 모든 PIL 이미지 모드, 닫힌 형태(closed-form) 값과 무차별 대입
(brute-force) 참조 구현 모두에 대한 SSIM, 데이터셋 워터마크 감지 가능성을 다룹니다.

비동기 테스트는 비동기 플러그인이 설치되어 있지 않으면 조용히 건너뛰지 않고 명확하게 실패합니다.

---

## 📖 인용 및 재사용

Apache-2.0 라이선스에 따라 사용, 수정, 재배포가 허용되며, 저작권 고지와
**Muhammad Rakibul Islam**에 대한 귀속 표시가 유지되어야 합니다.
[LICENSE](https://github.com/rakib-nyc/nullorigin/blob/HEAD/LICENSE) 및 [NOTICE](https://github.com/rakib-nyc/nullorigin/blob/HEAD/NOTICE)를 참조하십시오.

이 작업이 출판물에 활용된다면 다음과 같이 인용해 주세요:```bibtex
@software{islam_nullorigin_2026,
  author  = {Islam, Muhammad Rakibul},
  title   = {{NullOrigin}: Universal AI Provenance and Watermark
             Sanitization Middleware},
  year    = {2026},
  version = {1.2.0},
  url     = {https://github.com/rakib-nyc/nullorigin},
  note    = {Research artifact for watermarking robustness evaluation}
}

이 저장소는 완성된 연구 산출물로 게시되었으며 풀 리퀘스트를 받지 않습니다. 라이선스 조건에 따라 자유롭게 포크할 수 있습니다. 질문과 발견 사항은 [email protected]으로 이메일을 보내주시기 바랍니다.


🗺️ 프로젝트 상태

버전 1.0.0. 이 도구 모음은 사양대로 완성되었으며 완전히 테스트되었습니다. 알려진 한계는 다음과 같습니다:

  • SynthID, Tree-Ring, AudioSeal에 대한 효과성은 여기서 검증할 수 없습니다 — 이를 측정할 공개 탐지기가 없기 때문입니다. 교란은 구현되어 있지만, 효과에 대한 주장은 하지 않습니다.
  • 의미론적 드리프트는 불변식과 선택적 NLI로 제한됩니다. [nli] 없이는 모든 개체를 보존하는 역할 교체를 탐지할 수 없으며, 보고서에도 그렇게 명시되어 있습니다.
  • 로컬 추론 모델을 사용하면 지연 시간(구절당 약 150초) 때문에 텍스트 경로는 대화형 사용에 적합하지 않습니다. 더 작은 instruct 모델은 훨씬 빠릅니다.
  • TLS도 요청 인증도 없습니다 — 의도된 설계이며, 리버스 프록시 뒤에서 실행하십시오.

릴리스 기록은 CHANGELOG.md를 참조하십시오.


📚 참고 문헌

  • Kirchenbauer et al., A Watermark for Large Language Models (2023) — KGW 기법
  • Dathathri et al., Scalable watermarking for identifying large language model outputs (Nature, 2024) — SynthID-Text
  • Wen et al., Tree-Rings Watermarks: Invisible Fingerprints for Diffusion Images (2023)
  • San Roman et al., Proactive Detection of Voice Cloning with Localized Watermarking (2024) — AudioSeal
  • Krishna et al., Paraphrasing evades detectors of AI-generated text (2023)
  • Wang et al., Image Quality Assessment: From Error Visibility to Structural Similarity (2004) — SSIM
  • C2PA Specification

📜 법적 고지 및 저작자 표시

  • 저자 및 창작자: Muhammad Rakibul Islam
  • 연락처: [email protected]
  • 라이선스: Apache License, Version 2.0

이 저장소는 통계적 연구, 프라이버시 평가, 워터마킹 견고성 벤치마킹, 암호화 복원력 테스트를 위해 공개된 연구급 산출물입니다.```text Copyright 2026 Muhammad Rakibul Islam [email protected]

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

root@kitploit:~
http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

root@kitploit:~
**보증 없음.** 본 소프트웨어는 어떠한 종류의 보증도 없이 "있는 그대로" 제공되며,
명시적이거나 묵시적인 보증도 없습니다.
도구 다운로드
계층실제로 수행하는 작업
보이지 않는 문자완전히 효과적. 제로 너비, 양방향 제어, 변형 선택기 및 Unicode Tags 블록 페이로드가 완전히 제거되며 개수가 보고됩니다. 서로 다른 문자 체계 간 동형 글리프 혼동 문자(ASCII로 렌더링되는 키릴/그리스 문자)는 접힙니다.
문서 메타데이터(.docx)완전히 효과적. 작성자, 마지막 편집자, 수정 횟수, 타임스탬프, 템플릿 및 애플리케이션 버전이 docProps에서 제거되며 서식은 바이트 단위로 보존됩니다.
C2PA / EXIF / XMP완전히 효과적. 이미지가 원시 픽셀 샘플에서 새 컨테이너로 재구성되므로 서명된 JUMBF 매니페스트와 모든 메타데이터가 사라집니다. 태그된 픽스처에 대한 테스트로 검증됨.
KGW 통계 워터마크전적으로 재작성 백엔드에 달려 있음. 로컬 모델이 구성되지 않은 경우 통계 워터마크는 살아남습니다 — 이 도구는 그 반대를 암시하지 않고 이렇게 명시합니다.
SynthID-Text / SynthID-Image / Tree-Ring여기서는 검증 불가. 이들은 비공개 키와 독점 디코더를 사용합니다. NullOrigin은 문헌에 설명된 교란을 적용하지만, 실제 탐지기를 상대로 측정할 공개 탐지기가 없으므로 실제 탐지기를 무력화한다는 주장은 하지 않습니다.
AudioSeal / SynthID-Audio여기서는 검증 불가, 같은 이유로.
Compose v2를 포함한 Docker 20.10+
caselexical_f1embedding cosinebidirectional NLI
부정 생략됨0.700.77 ✓0.000 ✓
숫자 5 → 500.820.81 ✓0.000 ✓
개체/역할 교체0.810.985 ✗0.000 ✓
한정사 all → some0.880.91 ✓0.000 ✓
완곡 표현 제거0.270.953 ✗0.011 ✓
"must not" → "must"0.830.947 ✗0.000 ✓
충실한 재작성0.330.931 ✓0.998 ✓