Un link a una conversación compartida de ChatGPT bastaba para correr JavaScript ajeno dentro de la sesión de quien lo abría. Así lo documentó la firma de seguridad Parameter el 28 de octubre: armaron una cadena que empezaba en un share público y terminaba con acceso a la API autenticada de la víctima, incluidas sus conversaciones privadas. OpenAI lo corrigió el 26 de octubre, lo clasificó como P1 y pagó 5,000 dólares de recompensa. Si usas ChatGPT, no tienes que hacer nada. Si programas para la web, te toca más de lo que parece: el hueco no estaba en un modelo de IA, sino en un postMessage que no revisaba quién le escribía.
Qué podía hacer el ataque
Según Parameter, el atacante publicaba una conversación maliciosa con la función normal de compartir y mandaba el link. Al abrirla, el código corría como chatgpt.com con la sesión de la víctima. En su prueba controlada, el código listó las conversaciones recientes, descargó el contenido de una conversación privada que nunca se compartió con el atacante y mandó un fragmento a un servidor externo.
La víctima no tenía que instalar nada. En la versión más estable bastaba un clic en Preview; en la final, con el bloque HTML marcado para abrirse solo, abrir el link era el único paso.
Parameter afirma que con eso podía hacer "cualquier acción" en nombre de la cuenta. Lo que demostró, eso sí, fue la lectura de conversaciones; el resto es su deducción de que el código tenía el mismo acceso que la aplicación web. Tampoco reporta que alguien lo haya explotado fuera de su laboratorio.
La cadena, pieza por pieza
Lo interesante es que ninguna pieza estaba rota por sí sola:
- La gráfica. Una gráfica interactiva en la conversación cargaba MathLive, una librería de código abierto para capturar fórmulas. Al cargarse, instalaba en la página principal un listener de mensajes para su teclado virtual.
- El Preview. ChatGPT corre el HTML de los previews en un frame aislado en otro origen. Ese aislamiento funcionaba: el Preview no podía leer la página. Pero sí podía dejarle mensajes con
postMessage. - El listener confiado. El controlador de MathLive aceptaba mensajes de cualquier origen (política
none) y permitía cambiar el layout del teclado, que podía traer HTML. Ese HTML terminaba eninnerHTML. - El iframe heredero. Con ese HTML, Parameter metió un
iframeconsrcdocy sin atributosandbox. Como lo creaba la página principal, heredaba el origenchatgpt.com. - La CSP. La política de seguridad de contenido bloqueaba scripts inline pero permitía YouTube. El endpoint oEmbed de YouTube devolvía como JavaScript el parámetro
callbackaunque fuera inválido, y eso dio el script final.

Cómo se arregló
Según Parameter, el reporte llegó al bug bounty de OpenAI el 18 de octubre; el 21 OpenAI lo reprodujo y subió la severidad de P2 a P1; el 23 cambios en la CSP bloquearon parte de la cadena, y el 26 ambos frontends web rechazaban los mensajes de MathLive.
Aparte, el 31 de octubre salió MathLive 0.111.2 con una sección de seguridad: los mensajes del teclado virtual, el proxy y el mathfield ahora validan same-origin por defecto, los mensajes entre ventanas ya no pueden traer layouts personalizados "aunque la validación de origen esté desactivada", y los mensajes que cambian estado quedan atados a la ventana conectada. El changelog no menciona a ChatGPT ni a Parameter, así que no se puede afirmar que una cosa causó la otra. Ojo: la referencia de la API de MathLive todavía documenta none como valor por omisión de originValidator, así que conviene fijarlo explícitamente.
Lo que esto significa si haces web
Mi lectura: esto no es una historia de IA, es de integración. Cualquier sitio con librerías de terceros, iframes y una CSP con excepciones "de confianza" puede tener una cadena parecida. Lo práctico:
- Si usas MathLive, revisa tu versión con
npm ls mathlive, sube a 0.111.2 o más reciente y declaramathVirtualKeyboard.originValidator = 'same-origin'(o una función con tus orígenes permitidos) en lugar de confiar en el default. - Busca tus propios listeners:
grep -rnE "addEventListener\(.message|onmessage" src/. Cada uno debe revisarevent.originy tambiénevent.source. MDN lo dice sin rodeos: no revisarlos "habilita ataques de cross-site scripting". - Nunca aceptes HTML como configuración entre ventanas. Manda nombres de una lista fija.
- Iframes creados dinámicamente, con
sandbox, sobre todo si su contenido puede venir de otro frame. - Audita las excepciones de tu CSP. Un dominio grande permitido en
script-srcpuede tener un endpoint JSONP que convierta tu política en decorado. Parameter lo resume bien: la CSP es una defensa de respaldo, no la única barrera.
