Seguridad y confianza entre sitio y agente|INEMA.CLUB-PRO

Clase 4 · 18 min

Quién ve la tool: mismo origin, exposedTo, iframes y Permissions-Policy

Por defecto, solo la propia página y las del mismo origin ven la tool. Un iframe de otro origin solo aparece para la página superior con tres consentimientos, y un encabezado lo apaga todo. En esta clase mides los siete casos en Chromium y pones Permissions-Policy: tools=() en las páginas que no necesitan WebMCP.

Comenzar la clase
Dos ventanas de navegador superpuestas en un monitor oscuro, una dentro de la otra, con un conector entre ellas.

En esta clase vas a

  1. 1Origen = esquema + host + puerto, y el patrón de exposición de la spec
  2. 2Los tres consentimientos del iframe: allow="tools", exposedTo y fromOrigins
  3. 3Permissions-Policy: tools=() desactiva la API antes de cualquier script
  4. 4Siete casos medidos en Chromium 156 contra la spec del 16 de octubre de 2026

Objetivo: medir quién ve cada tool (mismo origin, iframe, exposedTo, fromOrigins) y apagar WebMCP con Permissions-Policy donde no debe existir.

01Los cuatro controles de exposición

Haz clic en cada uno. Top = la página principal; iframe = una página de otro origen dentro de ella.

Haz clic en cada bloque del diagrama para ver qué hace.

Medido en Chromium 156.0.8078.4 con la flag de prueba de WebMCP (experimental al 19 de octubre de 2026). La sección de fronteras de origen de la spec (§6.3.4) todavía es solo una nota pendiente.

02Siete casos, dos orígenes locales

El topo corre en 127.0.0.1 en un puerto y el iframe en 127.0.0.1 en otro puerto. Misma máquina, puerto distinto: origen distinto.

Terminal · exposición
cd ~/projetos/formacao-webmcp-seguranca-v72/kit
node scripts/exposicao.mjs; echo "saída: $?"
Archivo · kit/exposicao/filho.html
<!doctype html>
<!-- filho.html — a página dentro do iframe (pode ser de outra origem). Tenta registrar uma ferramenta.
     Parâmetro (só para o laboratório): ?exposto=<origem>  vira  registerTool(..., { exposedTo: [<origem>] }) -->
<meta charset="utf-8">
<title>iframe</title>
<script>
  (async () => {
    const q = new URLSearchParams(location.search);
    const mc = document.modelContext;
    let estado = 'sem WebMCP';
    if (mc) {
      try {
        const opcoes = q.get('exposto') ? { exposedTo: [q.get('exposto')] } : {};
        await mc.registerTool({ name: 'ferramenta_iframe', description: 'Ferramenta do iframe.',
          inputSchema: { type: 'object', properties: {} }, execute: async () => ({ ok: true }) }, opcoes);
        estado = 'registrou';
      } catch (e) { estado = 'recusou: ' + e.name; }
    }
    parent.postMessage({ iframe: estado }, '*');
  })();
</script>
Ejemplo del repositorio
== Chromium 156.0.8078.4 (Playwright) · modo: nativo
confere 0 · iframe da MESMA origem, sem nada → vê: mesma origem é o padrão
confere A · iframe de outra origem, sem allow="tools" → iframe recusado (NotAllowedError)
confere B · allow="tools", sem exposedTo → topo não vê
confere C · allow + exposedTo:[topo], topo sem fromOrigins → topo não vê
confere D · allow + exposedTo:[topo] + fromOrigins:[iframe] → topo VÊ a ferramenta do iframe
confere E · topo com Permissions-Policy: tools=() → topo e iframe recusados
confere F · exposedTo com origem http:// pública → iframe recusado (SecurityError)
7/7 casos conferem com a spec
saída: 0

Solo en el caso D la página superior ve la tool del iframe, y el resultado trae su origen (ferramenta_iframe@http://127.0.0.1:…). El agente puede usar ese campo para no confundir de quién es la tool.

03Reglas para tu sitio

1. Basta con el valor por defecto

Si la tool es solo de tu página, no uses exposedTo. El mismo origen ya la ve, y ningún otro la necesita.

04El encabezado en la práctica

El servidor del kit envía tools=() en la página de privacidad y nada en la página principal. Al lado, el mismo encabezado en tres servidores comunes.

Terminal · encabezado
cd ~/projetos/formacao-webmcp-seguranca-v72/kit
node scripts/servir.mjs & P=$!
until curl -s -o /dev/null http://127.0.0.1:8742/; do sleep 0.3; done
curl -sI http://127.0.0.1:8742/privacidade.html | grep -i '^permissions-policy'
curl -sI http://127.0.0.1:8742/ | grep -ci '^permissions-policy'
kill $P; echo "saída: $?"
Modelo · nginx
location = /privacidade.html {
    add_header Permissions-Policy "tools=()" always;
}
Modelo · Express
// antes do express.static
app.use('/privacidade.html', (req, res, next) => {
  res.set('Permissions-Policy', 'tools=()');
  next();
});
Modelo · _headers (Netlify, Cloudflare Pages)
/privacidade.html
  Permissions-Policy: tools=()
Ejemplo del repositorio
página em http://127.0.0.1:8742/
Permissions-Policy: tools=()
0
saída: 0

La página de privacidad manda tools=(); la principal manda 0 encabezados Permissions-Policy (necesita la API).

05Práctica

Medir y apagar.

E10 · Siete casos

5 min

Ejecuta el bloque exposición.

Listo cuando: 7/7 casos conferem com a spec y saída: 0.

E11 · Encabezado del kit

3 min

Ejecuta el bloque encabezado.

Listo cuando: Permissions-Policy: tools=() en la página de privacidad y 0 en la principal.

E12 · Tu sitio

20 min

Elige las páginas de tu sitio que no registran tools (login, pago, privacidad), configura el encabezado en tu servidor y revisa cada una con curl -sI [dirección de la página] | grep -i permissions-policy.

Listo cuando: Cada página de la lista responde con permissions-policy: tools=().

06Prueba de la clase

Pregunta 1 de 4

Un iframe de otro origen registró con exposedTo apuntando al documento superior, pero el documento superior no lo ve. ¿Qué falta?competencia

07En tres frases

PredeterminadoEl mismo origen ve; otro origen no ve nada.
IframeSolo con allow="tools", exposedTo y fromOrigins juntos.
Apagartools=() en las páginas que no necesitan la API.

Para ir más allá

Clase anteriorSiguiente clase