Como funciona a marca d'água em texto por IA e por que ela está sendo quebrada

Como funciona a marca d'água em texto por IA e por que ela está sendo quebrada

A Anthropic lançou marca d’água estatística em texto para o Claude na semana passada, com um post no blog explicando por que ela sobrevive a edições leves. Dias depois, um repositório no GitHub com licença MIT chamado watermarks-remover passou de 11.000 estrelas ao remover as marcas do Claude, o SynthID-Text do Google, superfícies de provenance da OpenAI e metadados C2PA de arquivos. Para quem constrói sobre LLMs, isso não é um drama de nicho: a marca d’água está se tornando comportamento padrão das plataformas, e o primeiro contra-ataque open-source sério expõe exatamente onde a tecnologia funciona e onde ela falha.

Como funciona a marca d’água estatística em texto

Esqueça os caracteres de largura zero. Uma marca d’água estatística não muda nada visível: o modelo continua emitindo tokens normais. O truque vive em como os tokens são amostrados.

Normalmente, um LLM rola um dado honesto a cada passo, amostrando da distribuição de probabilidade. Uma marca d’água do estilo KGW (a família à qual pertencem o SynthID-Text do Google e a marca d’água do Claude) troca esse dado por um determinístico: a aleatoriedade é substituída por um hash de uma chave secreta mais o contexto anterior. A analogia da própria Anthropic: a casa troca secretamente os dados pelos dígitos de Pi. Os jogadores continuam recebendo jogadas com aparência aleatória e a qualidade do jogo não muda, mas a casa, com a chave em mãos, pode reproduzir a sequência e pegar quem quer que a tenha usado.

# Simplified KGW-style watermarking
def sample(logits, key, prefix_hash):
    rng = PRNG(seed=hash(key, prefix_hash))  # deterministic, keyed
    green = pick_green_list(logits, rng, 0.5)  # about half the vocab
    if token in green:
        logits[token] += delta   # bias sampling toward the green list
    return sample_with_rng(logits, rng)

O verificador, com a chave em mãos, reproduz a mesma sequência e verifica se os tokens observados estão estatisticamente enviesados em direção à lista verde. Nenhum token extra, nenhum caractere oculto, e edições leves sobrevivem porque o viés vive nas estatísticas agregadas de escolha de palavras, não em um único token.

Por que ela falha: três superfícies de ataque

  • Artefatos Unicode — alguns pipelines ainda usam marcas físicas (Unicode invisível, espaços exóticos, caracteres de controle bidi, tag chars). São triviais de remover, e a remoção é sem perdas.
  • O sinal estatístico — a marca d’água vive na distribuição das escolhas de palavras. Se você parafrasear agressivamente, frase por frase, com um modelo diferente, o texto é re-rolado com outra distribuição e a chave deixa de bater. Essa é a fraqueza fundamental: a marca d’água é evidência de violação, não prova contra violação.
  • Provenance de arquivos — manifests C2PA, EXIF/XMP e propriedades de documentos embutidos em PNG/JPEG/SVG/PDF/DOCX/HTML/Markdown. Removíveis reconstruindo ou reemitindo o arquivo.

Por dentro do removedor com 11 mil estrelas

O repositório é deliberadamente sem glamour por fora: um agent skill mais um serviço Python usando apenas a stdlib. O skill é um cliente fino que aciona a maquinaria via HTTP, então um host de agente não precisa de Python nenhum. Internamente, são três camadas mais um harness de verificação:

  • Camada A — higiene de texto determinística: remove Unicode invisível, espaços exóticos, caracteres bidi e de tag. Sem perdas.
  • Camada B — o ataque estatístico: um agente chama um modelo não-fonte (Ollama local, por exemplo llama3.2, ou qualquer API) para reescrever o texto com um prompt de paráfrase, preservando o significado mas destruindo a distribuição de amostragem original. O autor admite que isso degrada um pouco o texto.
  • Camada C — limpeza em nível de arquivo: C2PA / EXIF / XMP / propriedades de documentos em vários formatos, usando c2patool, exiftool e qpdf (o qpdf faz uma reconstrução estrutural do PDF, necessária para uma remoção real em PDFs). A detecção por magic bytes faz as ferramentas de texto recusarem entrada binária, corrigindo o antigo bug em que limpar um .docx como texto corrompia o arquivo.
  • Marcas em pixels de imagem — backends pesados opcionais: CtrlRegen (ICLR 2025, ControlNet + DINOv2 IP-Adapter) regenera uma imagem semelhante em vez de pintar por cima; MarkDiffusion executa ataques de regeneração cega contra marcas d’água de difusão.
  • Loop de verificação — a parte da engenharia mais interessante: usa o harness MarkLLM da Tsinghua para aplicar uma marca d’água em um texto, limpá-lo e re-detectar. Só declara sucesso quando o detector reporta nenhuma marca d’água.

Na prática: inspecionar, limpar, servir

Apenas stdlib do Python 3.10+, sem dependências e sem Docker para o caminho principal:

# start the HTTP service
python3 service/scripts/server.py --host 127.0.0.1 --port 8765
# or simply: make serve

# inspect then clean any supported file
python3 service/scripts/inspect_file.py draft.md
python3 service/scripts/clean_file.py draft.md -o draft.cleaned.md
python3 service/scripts/clean_file.py photo.png -o photo.cleaned.png

# text-only Layer A, with stats
python3 service/scripts/clean_text.py draft.md -o draft.cleaned.md --stats

A API HTTP é a interface com a qual agentes e web apps se integram:

WM=http://127.0.0.1:8765
curl -s "$WM/health"            # {"ok": true, "version": ...}
curl -s "$WM/openapi.json"      # machine-readable contract

curl -s -X POST "$WM/clean" -H 'Content-Type: application/json' \
  -d '{"file":"<base64>","name":"notes.md"}'

A Camada B, por padrão, apenas imprime o prompt sem depender de nenhum modelo; conecte o Ollama quando realmente quiser a reescrita:

# default: print the rewrite prompt, no model required
python3 service/scripts/rewrite_text.py draft.md --backend print-prompt --strength paraphrase

# local Ollama, loopback only by default
WATERMARKS_REWRITE_BACKEND=ollama WATERMARKS_REWRITE_MODEL=llama3.2 \
python3 service/scripts/rewrite_text.py draft.md -o draft.rewritten.md

Para o stack completo, há imagens publicadas: ghcr.io/guillaumemeyer/watermarks-remover:latest (serviço principal mais limpadores), junto com as imagens de harness :markllm-latest e :markdiffusion-latest.

O que quem constrói deve fazer

  • Projete para evidência de violação, não para prova contra violação. Marcas estatísticas sobrevivem a edições leves, mas morrem diante de paráfrase em nível de frase. Se a sua história de compliance depende delas, você precisa de mais de uma camada.
  • Defesa em profundidade. Combine marcas estatísticas com C2PA em arquivos e atribuição no servidor (quem gerou o quê, quando, com qual chave). Mantenha as chaves no servidor; exponha apenas um endpoint de detecção.
  • Monitore a evasão. A remoção por reescrita ficou barata agora. Acompanhe as taxas de detecção no seu próprio conteúdo e trate uma queda como um sinal, não como uma surpresa.
  • Não exagere nas afirmações de detecção. O harness de verificação deste repositório existe justamente porque “removemos” só significa algo quando um detector confirma. Publique confiança e metodologia, não absolutos.

Uma ressalva honesta: remover marcas de provenance pode violar termos de serviço das plataformas e, em algumas jurisdições, a lei. Use isto apenas em conteúdo seu e onde for permitido.

Recursos

Notícias Relacionadas