본문으로 건너뛰기
effidevFlutter · Cloudflare 엣지 · 클라우드 비용 최적화

그림판 워터마크, 서버가 준 번호가 픽셀에 박힌다

effidev

윈도우 그림판으로 그림을 그려 본 적이 있다면, 요즘 버전에 AI 기능이 붙어 있다는 걸 아실 겁니다. 대충 스케치를 하고 문장으로 설명을 적으면 그럴듯한 그림으로 바꿔 주는 기능입니다.

이 글은 그 기능으로 만든 그림 안에 무엇이 같이 들어가는지를 다룹니다. 컴퓨터를 잘 모르셔도 됩니다. AI로 그림을 한 번이라도 만들어 봤거나, 만들 생각이 있는 분이라면 알아 둘 값어치가 있는 이야기입니다.

결론부터 말씀드리면 이렇습니다. “내 PC의 칩에서 돌아간다”고 안내되는 방식으로 만들어도, 그림을 만들기 전에 내가 쓴 문장이 마이크로소프트 서버로 한 번 갑니다. 그리고 서버가 내려준 번호 하나가 완성된 그림의 픽셀 속으로 들어갑니다.

파일 정보 칸에 적히는 게 아닙니다. 그림 자체가 미세하게 바뀝니다. 눈으로는 전혀 알 수 없습니다.

이 사실은 마이크로소프트가 발표한 게 아니라, Xusheng Li라는 개발자가 2026년 8월 20일에 그림판 프로그램을 통째로 뜯어보고 공개한 분석에서 나왔습니다. 아래 내용은 그 분석 원문을 따라간 것이고, 확인된 숫자와 이름만 옮깁니다.

보이는 워터마크와 뭐가 다른가

본론에 들어가기 전에 단어부터 정리하겠습니다. 워터마크라고 하면 보통 사진 구석에 반투명하게 얹힌 로고나 글자를 떠올리게 됩니다. 무료 편집 앱으로 저장하면 붙는 그런 것 말입니다.

이 글에서 다루는 건 그게 아닙니다. 눈에 전혀 보이지 않는 워터마크입니다.

원리는 이렇습니다. 그림은 결국 아주 작은 색점(픽셀) 수십만 개가 모인 격자입니다. 색점 하나하나는 숫자로 표현됩니다. 그 숫자를 아주 조금씩만 바꾸면, 사람 눈은 차이를 못 느끼지만 프로그램은 그 미세한 패턴을 읽어낼 수 있습니다.

정보를 그런 식으로 그림에 섞어 넣는 걸 스테가노그래피, 우리말로는 심층 암호라고 부릅니다. 그림판이 쓰는 방식도 그 계열입니다.

여기서 세 가지가 갈립니다.

세 번째가 이번 이야기의 대상입니다. 그림을 다른 이름으로 다시 저장하거나 화면을 캡처해도 그림의 모양이 크게 유지되는 한 값이 살아남도록 설계돼 있습니다.

그래서 “이거 그냥 다른 형식으로 저장하면 되는 거 아니야?“라는 대응이 통하지 않습니다. 파일 정보는 그렇게 지워지지만, 픽셀에 섞인 값은 그림 자체에 들어 있기 때문입니다.

전원선을 뽑아도 그림은 서버를 거친다

먼저 용어 하나만 풀고 가겠습니다. 그림판의 AI 그림 기능은 두 갈래입니다.

하나는 Image Creator입니다. 이건 처음부터 클라우드, 그러니까 마이크로소프트 서버에서 그림을 만들어 내려받는 방식입니다. 인터넷이 필요하다는 게 당연하게 느껴집니다.

다른 하나가 Cocreator입니다. 이쪽이 문제의 기능입니다. Copilot+ PC라고 부르는, AI 연산 전용 칩(NPU)이 달린 노트북에서 그림 생성을 내 컴퓨터 안에서 처리한다고 안내됩니다.

“내 기기에서 돈다”는 말은 보통 이렇게 읽힙니다. 내가 뭘 그렸는지, 뭐라고 적었는지가 밖으로 안 나간다는 뜻으로요.

그런데 분석 결과는 달랐습니다. Cocreator로 그림을 만들 때 순서는 이렇습니다.

  1. 내가 적은 문장이 마이크로소프트 서버의 심의 창구로 전송됩니다.
  2. 서버가 다듬은 문장(revisedPrompt)과 번호 하나를 돌려줍니다.
  3. 그 다듬어진 문장으로 내 컴퓨터의 칩이 그림을 만듭니다.
  4. 만들어진 그림에 2번에서 받은 번호가 심어집니다.
  5. 그림이 다시 서버로 올라가 서명을 받고 내려옵니다.

즉 그림을 만드는 계산 자체는 내 컴퓨터에서 하는 게 맞습니다. 하지만 앞뒤로 서버를 두 번 거칩니다. 그리고 그 사이에 받은 번호가 그림 안에 남습니다.

로컬에서 도는 AI가 무조건 폐쇄적이지 않다는 건 로컬 LLM 실측 글에서 다룬 “같은 모델이라도 환경에 따라 결과가 갈린다”는 이야기와는 또 다른 결입니다. 여기서는 결과가 아니라 경로가 예상과 달랐습니다.

뜯어본 사람이 연 파일들

분석은 감이 아니라 파일을 직접 열어서 진행됐습니다. 어떤 파일을 봤는지가 그대로 적혀 있습니다.

대상 버전은 그림판 11.2605.71.0(64비트), **사진 앱 2026.11060.2004.0(64비트)**입니다.

그림판이 설치된 자리는 이렇습니다.

C:\Program Files\WindowsApps\Microsoft.Paint_11.2605.71.0_x64__8wekyb3d8bbwe\PaintApp\

여기서 핵심 파일이 Watermarker.dll입니다. 크기가 1.67MB입니다. 같은 이름의 파일이 사진 앱 폴더에도 들어 있습니다.

C:\Program Files\WindowsApps\Microsoft.Windows.Photos_2026.11060.2004.0_x64__8wekyb3d8bbwe\Watermarker.dll

같은 장치가 두 앱에 다 들어가 있다는 뜻입니다.

폴더에는 AI 모델 파일도 네 개 들어 있었는데, 확장자가 .onnxe로 끝나는 암호화된 상태였습니다.

파일 크기 역할
seg.onnxe 23.1MB 영역 나누기
inseg_enc.onnxe 28.0MB 인코딩
inseg_dec.onnxe 16.5MB 디코딩
mager.onnxe 302.4MB 그림 생성

암호를 푸는 방식이 허술한 편이었습니다. segapi.dll 안에 키가 그대로 들어 있고, 그 키로 XOR이라는 단순 연산을 하면 풀립니다. 1.0.80 버전 키는 Microsoft_2023이라는 평범한 문자열이었고, 1.0.81 버전은 4,096바이트짜리 영숫자 문자열이었습니다.

풀고 나니 표준 ONNX 파일이 되어서, 검증 도구(onnx.checker.check_model())를 그대로 통과했다고 합니다.

여기까지가 “그림을 어떻게 만드나”였고, 진짜는 그다음입니다.

그림보다 문장이 먼저 나간다

프로그램이 어떤 순서로 함수를 부르는지가 추적돼 있습니다. 옮기면 이렇습니다.

PaintUI.dll
  → IPromptModerationService
    → PaintAIManager.dll
      → AIServices.dll!ModerateAsync()      ← 여기서 서버로 전송
      → AIServices.dll!ParseModerateResponse()
      → Paint::AI::StableDiffusionHelpers::GenerateAsync()   ← 여기서 내 칩이 그림 생성
        → Paint::AI::AddWatermark(bitmap, winrt::guid const&)
          → Watermarker.dll!WmkWriteWatermark()              ← 여기서 픽셀에 심기

중요한 건 순서입니다. 그림을 만드는 함수보다 서버로 보내는 함수가 먼저입니다.

전송되는 주소도 잡혔습니다.

https://apsaiservices-a0fqcjc6bzbhgdcd.b02.azurefd.net/v1/paint-cocreator/moderate-prompt

보내는 내용은 세 가지입니다. 내가 적은 문장(prompt), 고른 그림체(style), 직전 생성 번호(lastPromptGenerationId).

돌아오는 내용은 네 가지입니다.

{
  "revisedPrompt": "...",
  "promptGenerationId": "74d9e06b-adea-43ce-85fe-186a26e2e34a",
  "watermarkId": "83424621-03cb-40e3-9808-a9fae837156d",
  "containsHumanReference": false
}

watermarkId가 그림에 심길 번호입니다. 내 컴퓨터가 만든 게 아니라 서버가 발급한 값이라는 게 이 글의 핵심입니다.

containsHumanReference는 그 문장에 사람에 대한 언급이 들어 있는지를 서버가 판정한 결과입니다. 심의 창구가 문장 내용을 실제로 읽고 분류한다는 뜻이기도 합니다.

그림이 완성된 뒤에는 두 번째 통신이 있습니다. 같은 서버의 /v1/paint-cocreator/image-sign으로 그림과 부가 정보가 올라갑니다. 부가 정보에는 생성 번호, 난수 씨앗값(GenerationSeed), 창의성 수준(CreativityLevel), 버전, 심의 점수가 담깁니다. 서버는 그걸 받아 출처 증명서를 만들어 돌려줍니다.

정리하면 이렇습니다. 그림판 워터마크는 내 컴퓨터 혼자 만드는 값이 아니라, 서버가 내 문장을 본 다음 발급하고, 서버가 다시 서명까지 하는 값입니다.

픽셀 열아홉 만 개를 건드리는 방식

이제 심는 방법입니다. 여기가 이 분석에서 가장 구체적인 부분입니다.

받은 번호는 16바이트입니다. 프로그램은 여기에 앞뒤로 하나씩 덧붙입니다.

그래서 실제로 심는 값은 1 + 16 + 1 = 18바이트가 됩니다. 이걸 다시 비트 단위로 펼치면 144개가 됩니다. 비트는 0 아니면 1인 가장 작은 정보 단위입니다.

이 144개를 그림 전체에 흩뿌립니다. 규칙은 이렇습니다.

8픽셀 단위로 내림하는 이유는 이 방식이 낱개 픽셀이 아니라 8×8 블록 단위로 값을 조정하기 때문입니다. JPEG 압축이 그림을 8×8 조각으로 나눠 처리하는 것과 같은 크기인데, 압축을 거쳐도 블록 경계가 어긋나지 않아 값이 덜 깨집니다.

한 군데만 심으면 그림을 조금만 손대도 값이 날아갑니다. 세 군데 이상 심는 건 찌그러져도 살아남게 만들려는 설계입니다. 같은 정보를 여러 벌 심어 두고 나중에 다수결로 읽는 방식이라고 보시면 됩니다.

그래서 실제로 얼마나 바뀌느냐. 분석에서 가로세로 512짜리 그림을 넣고 재 봤습니다.

항목
그림 전체 픽셀 수 262,144개
워터마크 후 바뀐 픽셀 수 193,376개
비율 약 73.8%

픽셀 열에 일곱은 값이 달라졌습니다. 그런데 눈으로는 차이를 못 느낍니다. 각 픽셀에서 바뀌는 폭이 아주 작기 때문입니다.

숫자를 조금 더 와닿게 옮겨 보겠습니다. 512×512는 스마트폰 화면 한 귀퉁이만 한 작은 그림입니다. 그 작은 그림 안에서만 19만 개가 넘는 점이 조용히 조정된 겁니다. 요즘 흔한 2048×2048 그림이라면 같은 비율로 300만 개가 넘어갑니다.

클라우드로 만들 때는 분업이 통째로 다르다

앞에서 AI 그림 기능이 두 갈래라고 말씀드렸습니다. 지금까지 본 건 내 칩으로 만드는 Cocreator 쪽이었고, 클라우드 쪽은 순서가 다릅니다.

Cocreator (내 칩) Image Creator (클라우드)
문장 심의 서버 서버
그림 생성 내 컴퓨터의 AI 칩 서버
워터마크 심기 내 컴퓨터에서 (Watermarker.dll) 서버
증명서 서명 서버 서버
내 PC에 도착할 때 표식이 아직 없는 상태 표식이 이미 붙은 상태

클라우드 경로는 단순합니다. 서버가 전부 처리해서 워터마크와 증명서가 이미 박힌 완성품을 내려보냅니다. 그림판은 받아서 보여 주기만 하면 됩니다.

Cocreator가 복잡한 건 그림이 내 컴퓨터 안에서 만들어지기 때문입니다. 서버는 그 그림을 본 적이 없으므로 대신 심어 줄 수가 없습니다. 그래서 번호만 먼저 발급해 두고, 심는 일은 내 컴퓨터에 시키는 구조가 됐습니다.

분석자는 이 점을 두고 “지역과 클라우드 경로의 차이가 이 이상한 분업을 설명해 준다”고 적었습니다. 클라우드는 완성품을 줄 수 있지만, Cocreator는 로컬 추론이 끝난 뒤에 서버가 발급한 식별자를 뒤늦게 심어야 한다는 겁니다.

뒤집어 보면 이런 뜻이기도 합니다. 그림판에 워터마크 장치(Watermarker.dll)가 굳이 들어 있는 이유 자체가, 로컬 생성 경로가 존재하기 때문입니다. 클라우드만 있었다면 이 파일은 필요 없었습니다.

심을 자리가 모자라면 벌어지는 일

모든 그림에 다 심을 수 있는 건 아닙니다. 조건이 있습니다.

가로세로가 192픽셀 이상이어야 합니다. 144개 비트를 각각 세 군데 이상 심으려면 최소한의 면적이 필요하기 때문입니다.

조건이 안 맞으면 프로그램이 오류 번호를 냅니다. 심을 값이 16바이트보다 짧으면 −6, 길면 −5, 그림에 자리가 모자라면 −8입니다.

여기서 재미있는 차이가 나옵니다. 같은 실패를 두 앱이 다르게 처리합니다.

워터마크 실패 시
그림판 그림 생성 자체를 실패로 처리 — 그림을 내주지 않음
사진 앱 오류를 기록만 하고 그림은 그대로 내줌

그림판 쪽 설계는 분명합니다. 표식을 못 넣을 바에는 결과물을 주지 않겠다는 겁니다. 사용자 입장에서는 “왜 안 만들어지지?” 싶은 순간이 사실은 워터마크 실패일 수 있다는 뜻이기도 합니다.

저장 형식도 제한됩니다. PNG, JPEG, GIF, 그리고 그림판 전용 .paint만 허용되고 BMP는 빠졌습니다. BMP가 출처 정보를 담을 자리가 없는 형식이라 그렇습니다.

겉면과 속, 두 겹으로 남는 표식

여기서 C2PA라는 이름이 나옵니다. 처음 들으셨다면 이렇게 이해하시면 됩니다. 사진이나 그림에 “이건 누가 언제 어떻게 만들었다”를 적어 두는 국제 표준 규격입니다. 콘텐츠 자격증명(Content Credentials)이라고도 부릅니다.

그림판이 만든 그림에는 이 증명서가 파일에 붙습니다. 여기까지는 마이크로소프트가 공개적으로 설명해 온 내용입니다.

새로 밝혀진 건 그 증명서 안에 들어 있는 항목입니다. c2pa.soft-binding이라는 칸에 픽셀에 심은 그 번호가 그대로 적혀 있고, 사용한 알고리즘 이름이 com.microsoft.invismark.1로 표기돼 있습니다.

soft-binding이 왜 중요하냐면, C2PA 규격에서 이 칸의 목적이 **“파일에서 증명서를 지워도 다시 찾아낼 수 있게 하는 것”**이기 때문입니다. 파일 정보는 지우기 쉽지만 픽셀에 섞인 값은 남으니까요.

그래서 표식이 두 겹이 됩니다.

증명서를 떼어 낸 그림이 돌아다니더라도, 픽셀 속 번호만 읽으면 원래 증명서를 찾아올 수 있는 구조입니다.

마이크로소프트가 적어 둔 것과 비워 둔 것

이 대목은 공정하게 나눠 볼 필요가 있습니다. 마이크로소프트가 거짓말을 한 건 아닙니다.

공개 문서에 적혀 있는 것:

분석자가 지적한 건 적혀 있지 않은 부분입니다.

세 번째가 특히 체감이 다릅니다. 파일 정보라면 “저장할 때 지우면 되지”라고 생각할 수 있지만, 픽셀에 섞인 값은 일반 사용자가 손댈 방법이 사실상 없습니다. 분석에서도 프로그램 파일을 직접 고치지 않고 끄는 방법은 발견되지 않았다고 적고 있습니다.

분석자는 배경으로 EU AI법 50조를 언급합니다. 2026년 8월 2일부터 AI로 만든 콘텐츠에 기계가 읽을 수 있는 표식을 붙이도록 요구하는 조항입니다. 다만 마이크로소프트의 의도가 그것인지는 확인할 수 없다고 분명히 선을 그었습니다. 저도 여기서는 같은 선을 지키겠습니다.

기밀로 다뤄지는 값이 예상 밖의 곳에 남아 있는 문제는 이 사례만의 것이 아닙니다. 추론 트레이스 유출 사례에서도 “당연히 안 나갈 것”으로 여겼던 데이터가 경로 하나 때문에 밖에 남았습니다. 공통점은 설명된 동작과 실제 동작 사이의 틈입니다.

내 그림 안을 직접 열어 보는 법

남의 분석을 그대로 믿을 필요는 없습니다. 직접 확인할 수 있습니다.

그림판으로 AI 그림을 만들어 PNG로 저장한 다음, 그 파일의 앞부분을 살펴보면 됩니다. PNG 파일은 안이 덩어리(chunk) 단위로 나뉘어 있는데, 파일 맨 앞 IHDR이라는 덩어리 바로 뒤에 caBX라는 덩어리가 붙어 있습니다. 그 안에 증명서가 들어 있습니다.

증명서를 텍스트로 펼치면 c2pa.soft-binding.value 항목이 보이고, 거기에 그 번호가 그대로 적혀 있습니다. 분석 원문에 예시로 나온 값이 83424621-03cb-40e3-9808-a9fae837156d입니다.

프로그램을 새로 깔기 싫으시면 이렇게 보셔도 됩니다.

여기서 나오는 값과, 다른 날 만든 그림의 값을 비교해 보시면 그림마다 번호가 다르다는 것도 직접 볼 수 있습니다.

이번 분석의 사정거리 밖

과장하지 않기 위해, 이 분석이 다루지 않은 것도 적어 둡니다.

세 번째가 특히 중요합니다. 픽셀에 번호가 있다는 것과, 그 번호로 나를 특정할 수 있다는 것은 다른 이야기입니다. 지금 확인된 건 앞쪽까지입니다.

내일부터 달라지는 습관 세 가지

읽고 나서 실제로 바꿀 만한 것만 세 개 남기겠습니다.

첫째, “로컬”과 “오프라인”을 구분해서 읽으십시오. 이제 제품 설명에 “내 기기에서 처리”라고 적혀 있어도, 그게 “아무것도 전송되지 않는다”는 뜻은 아닙니다. 이번 경우는 계산은 로컬, 심의와 표식은 서버였습니다. 진짜로 궁금하면 인터넷을 끊고 그 기능이 되는지 시험해 보는 게 가장 빠릅니다.

둘째, 회사 일로 AI 그림을 만든다면 그림판 워터마크를 전제하고 쓰십시오. 시안이나 내부 자료를 그림판 AI로 만들어 외부에 보내면, 그 그림에는 생성 시점의 식별자가 붙어 나갑니다. 문제가 된다는 뜻이 아니라, 모르고 나가는 것과 알고 나가는 것은 다르다는 뜻입니다.

셋째, 내가 쓰는 AI 그림 도구가 뭘 심는지 한 번은 확인해 두십시오. 위의 caBX 확인 방법은 그림판뿐 아니라 C2PA를 지원하는 다른 도구에도 똑같이 통합니다. 5분이면 됩니다.

마지막으로 한 가지만 덧붙이겠습니다. 이 구조 자체가 나쁘다고 단정할 일은 아닙니다. AI로 만든 그림이 진짜 사진 행세를 하는 문제를 막으려면 어떤 형태로든 표식이 필요하고, 그 표식이 쉽게 지워지면 의미가 없습니다.

문제는 방향이 아니라 설명의 부족입니다. 픽셀 열에 일곱을 건드리는 작업이라면, 그게 무엇이고 어디서 온 값인지는 제품 설명에 적혀 있는 편이 낫습니다.

여러분은 어떠신가요. AI 그림에 지워지지 않는 표식이 붙는 것, 안전장치로 보이시나요 아니면 감시로 보이시나요.