---
type: explanation
title: Como a Caputchin coloca os jogos em sandbox
summary: "Cada medida de isolamento ao redor de um jogo Caputchin: o iframe de origem opaca, seu único token de sandbox, a Content-Security-Policy inline estrita, o hashing de integridade, e como tudo isso coexiste com uma CSP que você define na sua própria página."
---

Um jogo Caputchin é código de terceiros que roda no navegador do seu visitante, na sua página. Quer venha do marketplace, quer você mesmo o hospede, a Caputchin o trata como **não-confiável por padrão** e o envolve em várias camadas de isolamento independentes, de modo que um jogo bugado ou hostil possa renderizar, jogar e reportar um resultado, mas não consiga alcançar mais nada: nem sua página, nem os dados do seu visitante, nem a rede.

Esta página explica cada medida, por que cada uma existe, e como tudo interage com uma CSP que você adiciona ao seu próprio site.

## O modelo de ameaça em uma linha

O jogo roda JavaScript arbitrário (e possivelmente WebAssembly) fornecido por um terceiro. A meta de isolamento é, portanto: **o jogo pode computar e desenhar, e pode entregar um resultado de volta ao widget, mas não pode ler nem afetar nada fora do seu próprio frame.** Cada camada abaixo serve a essa única meta, e elas são defesa em profundidade, nenhuma camada sozinha é confiada como perfeita.

```mermaid
flowchart TD
  P["Sua página (sua origem)"] --> W["Widget Caputchin (sua origem)"]
  W -->|constrói o iframe| F["Iframe em sandbox (origem null)"]
  F --> G["O jogo: JS / WASM"]
  G -->|só resultado por postMessage| W
  subgraph Dentro do frame
    G
    L1["sandbox=allow-scripts apenas"]
    L2["Content-Security-Policy inline"]
    L3["Subresource Integrity (marketplace)"]
  end
```

## Camada 1: um iframe em sandbox de origem opaca

Todo jogo roda dentro de um `<iframe>` que o widget constrói com um atributo `sandbox` deliberadamente mínimo. O **único** token concedido é `allow-scripts`, porque o jogo é JavaScript e precisa rodar. Tudo o mais é retido, e a omissão mais importante é `allow-same-origin`.

Sem `allow-same-origin` o navegador dá ao frame uma **origem null** única. Esse único fato é a fronteira de sustentação:

- O jogo não consegue ler o DOM, os cookies, o `localStorage` da sua página, nem a sessão Caputchin, ele está selado em uma origem descartável que é estranha à sua.
- Como o frame também nunca consegue navegar sua janela de topo, abrir popups, enviar formulários, ou abrir diálogos nativos (todas essas capacidades são tokens de sandbox que a Caputchin não concede), não há caminho do jogo de volta para fora até sua página.

A combinação de **`allow-scripts` sem `allow-same-origin`** é o ponto inteiro: o navegador executa o código do jogo mas trata o frame como uma origem estranha que não pode tocar em nada seu. A Caputchin nunca adiciona `allow-same-origin` a um frame de jogo. (Como consequência, toda mensagem que o jogo posta ao widget chega da origem `"null"`, que as checagens de canal do widget esperam, uma origem não-null em si sinalizaria uma sandbox mal configurada.)

## Camada 2: o documento do frame é construído inline, não buscado

O widget não aponta o iframe para uma página remota. Ele constrói o documento HTML inteiro do frame **inline** (via `srcdoc`) e o entrega ao frame de origem null. Esse documento é minúsculo: uma meta tag de CSP estrita (próxima camada), um pequeno bootstrap do runtime Caputchin, e uma tag `<script>` que carrega o bundle do jogo.

Este é o mesmo mecanismo para os dois caminhos de entrega, só a URL do bundle difere:

| Origem do jogo | URL do bundle no documento inline |
|---|---|
| Marketplace (resolvido pela plataforma) | A URL do bundle fixada, com hash de integridade, que a Caputchin resolveu na montagem. |
| Auto-hospedado (seu `game-src`) | A URL que você forneceu. |

Como o documento é criado pelo widget em vez do jogo, o jogo nunca controla a CSP, o runtime, ou a estrutura do frame, só o que seu próprio bundle faz uma vez carregado.

## Camada 3: Subresource Integrity para jogos de marketplace

Quando a Caputchin serve um jogo de marketplace ela fixa o bundle em uma versão imutável e registra um hash criptográfico (SHA-384) dele. A tag `<script>` que carrega o bundle carrega esse hash como um atributo `integrity` com `crossorigin="anonymous"`, então o **próprio navegador** se recusa a executar o bundle se um único byte diferir do que foi fixado. Um CDN comprometido não pode substituir código diferente; o carregamento falha de forma fechada.

Um jogo auto-hospedado não tem hash afirmado pela plataforma (você é a raiz de confiança dos seus próprios bytes), então o atributo `integrity` é simplesmente omitido para esse caminho. Veja [o contrato de replay](/docs/marketplace-game-development/replay-contract) para como o *resultado* de um jogo de marketplace é rederivado de forma independente no servidor, uma garantia separada da integridade.

## Camada 4: uma Content-Security-Policy inline estrita

O documento inline carrega uma `Content-Security-Policy` apertada. Mesmo que o frame já seja de origem null e em sandbox, a CSP restringe ainda mais o que o código carregado pode fazer, ela nega tudo por padrão e reconcede só o mínimo:

| Diretiva | Valor | Por quê |
|---|---|---|
| `default-src` | `'none'` | Nega tudo que não for explicitamente permitido abaixo. |
| `script-src` | um hash do próprio runtime inline da Caputchin + a origem do bundle do jogo + `'wasm-unsafe-eval'` | Roda só o bootstrap exato da Caputchin (fixado por um hash `sha256-`, **não** `'unsafe-inline'`) e o próprio bundle do jogo. `'wasm-unsafe-eval'` deixa um motor WASM compilar **sem** conceder `'unsafe-eval'` completo. |
| `connect-src` | `'none'` | **A diretiva anti-exfiltração.** O jogo não pode `fetch`, `XHR`, abrir um `WebSocket`, nem fazer beacon para lugar nenhum. Nenhuma saída de rede de jeito nenhum. |
| `img-src` | `data:` (mais quaisquer origens de ativo de skin, abaixo) | Sprites inline, ou de um host de ativo permitido. |
| `media-src` | `data:` (mais quaisquer origens de ativo de skin, abaixo) | Áudio/vídeo inline, ou de um host de ativo permitido. |
| `font-src` | `data:` | Só fontes inline. |
| `style-src` | `'unsafe-inline'` | Os jogos definem estilos inline; sem folhas de estilo externas. (Estilos não conseguem exfiltrar dados do jeito que script ou rede conseguem.) |

O efeito líquido: o jogo roda, renderiza e reporta seu resultado pela ponte do SDK, e não consegue alcançar mais nada, especialmente não a rede. Esta CSP é definida pela Caputchin e não é algo que você configura; ela é listada aqui para que você entenda exatamente quão confinado um jogo está.

### O único lugar onde uma configuração do cliente alarga a CSP do frame

Os skins podem apontar campos de imagem ou áudio para uma URL absoluta no seu próprio CDN (veja [skins](/docs/game-customization/skin)). Para que esses ativos carreguem dentro do frame trancado, a Caputchin adiciona **só essas origens de ativo exatas** ao `img-src` / `media-src` do frame, e em nenhum outro lugar. `script-src` e `connect-src` nunca são alargados, então mesmo uma origem de ativo permitida não pode ser usada para carregar código ou abrir um canal de rede. Esta é a única forma estreita em que um valor configurado afeta a política do frame, e ela ainda é só para ativos.

## Camada 5: um canal de resultado de mão única

O jogo não tem nenhuma superfície chamável para dentro da sua página. A única saída é a ponte do SDK: o jogo chama um único método que faz `postMessage` do seu resultado opaco (seu trace) ao widget, que vive na sua origem. O widget é o que fala com a API da Caputchin; o jogo nunca o faz (ele não consegue, `connect-src` é `'none'`). Então o caminho de confiança é jogo → widget → seu backend, e cada salto só passa um resultado adiante, nunca concede ao jogo alcance para trás.

## A CSP que você define na sua própria página [#the-csp-you-set-on-your-own-page]

As camadas acima protegem *você e seu visitante do jogo*. Uma Content-Security-Policy na sua própria página é um controle diferente e complementar: ela protege *sua página de tudo*, e a Caputchin é projetada para rodar sob uma estrita. Você não precisa afrouxar sua CSP para embutir a Caputchin; você precisa **permitir as poucas origens que o widget legitimamente usa.**

No mínimo, o widget precisa de:

| Diretiva | Permitir | Por quê |
|---|---|---|
| `script-src` | a origem da qual você carrega o script do widget, mais `'unsafe-eval'` | O `<script>` que define `<caputchin-widget>` / `<caputchin-game>`, o [CDN](/docs/integration-guides/cdn) que você escolheu (jsDelivr, seu próprio host, ou `caputchin.com`). `'unsafe-eval'` deixa o desafio de [instrumentação](/docs/understanding-caputchin/instrumentation) rodar; ele é exigido enquanto a instrumentação está ligada, e você pode largá-lo desligando a instrumentação na página de [Segurança](/docs/site-keys/security) da chave. |
| `connect-src` | `https://verify.caputchin.com` | O widget chama o serviço de verificação da Caputchin para montar e confirmar uma verificação. Permita exatamente esta origem. Se você aponta o widget para um host de API diferente, permita esse em vez disso. |
| `frame-src` | `https://caputchin.com` | Governa o iframe do jogo. Como o frame usa um documento `srcdoc` inline, os navegadores aplicam seu `frame-src` a ele, então permita a origem Caputchin aqui. |
| `worker-src` | `blob:` | O solver de proof-of-work roda inteiramente em Web Workers criados a partir de uma URL `blob:`, sem fallback na thread principal, então isto é exigido. Se você omite `worker-src`, os navegadores recaem em `child-src` depois `default-src`, e `default-src 'self'` sozinho não permite `blob:`. |

Opcionalmente adicione `'wasm-unsafe-eval'` ao `script-src`: ele deixa o solver de proof-of-work rodar como WebAssembly rápido. Não é exigido, o solver recai em uma implementação mais lenta em JavaScript puro (ainda dentro do Worker) sem ele.

Você **não** precisa permitir a própria origem do bundle do jogo (jsDelivr, o CDN de um jogo, seu host `game-src`) na CSP da sua página: esse bundle carrega *dentro do frame em sandbox*, sob a própria CSP do frame descrita acima, não sob a política da sua página. Sua página só emoldura a Caputchin; a Caputchin confina o que roda dentro.

Se um elemento Caputchin silenciosamente falha em carregar sob sua CSP, o console do seu navegador nomeia a diretiva bloqueada; adicione a origem ou a palavra-chave que ele nomeia a essa diretiva. Comece estrito e abra exatamente o que o console pedir. Você nunca precisa de `'unsafe-inline'`. Você precisa de `blob:` para os workers (`worker-src`) e, enquanto a instrumentação está ligada, de `'unsafe-eval'` em `script-src`; o requisito de `'unsafe-eval'` some se você desligar a instrumentação na página de [Segurança](/docs/site-keys/security) da chave. `'wasm-unsafe-eval'` é opcional (ele acelera o solver de proof-of-work, que de outra forma roda mais devagar em JavaScript dentro do mesmo Worker).

## Por que tantas camadas

Cada camada seria uma fronteira significativa por si só; juntas elas significam que uma falha em uma não é uma brecha. A origem null sozinha já isola o jogo dos seus dados; a CSP sozinha já mata a saída de rede; a integridade sozinha já fixa os bytes exatos. A Caputchin roda todas elas porque código de terceiros não-confiável é exatamente o lugar onde a defesa em profundidade vale o investimento.

## Veja também

- [Como os jogos resistem a bots](/docs/understanding-caputchin/gaming-anti-cheat): o lado autoritativo no servidor da integridade do jogo.
- [O contrato de replay](/docs/marketplace-game-development/replay-contract): como o resultado de um jogo é rederivado de forma independente no servidor.
- [Construir um jogo auto-hospedado](/docs/custom-game-development/self-hosted-game): as restrições de bundle que estas sandboxes impõem aos autores.
- [Carregar o widget de um CDN](/docs/integration-guides/cdn): escolher a origem de script que a CSP da sua página precisa permitir.
