Quick Answer: A ChatGPT account takeover through a shared link means a public conversation ran attacker JavaScript inside the victim's signed-in chatgpt.com session. Parameter's researchers chained a Preview sandbox boundary, an unvalidated postMessage listener, and a Content Security Policy bypass to read private chats. OpenAI fixed the issue on October 26, 2026, and rated it P1.
A link that opens a chat transcript shouldn't be able to read your other conversations. In October 2026, though, researchers at Parameter showed that one public ChatGPT share could do exactly that. The victim's only action was opening the link. The payload ran as chatgpt.com, listed the victim's conversations, and pulled message content from a private chat that had never been shared with anyone. Here's how the chain worked, why the sandbox wasn't the weak point, and what this means for your account.
What the Researchers Actually Found
According to Parameter's write-up, the attacker only needed to create a conversation, publish it with ChatGPT's normal sharing feature, and send the link. No app install, no Project join, no pasted code, no developer tools. The chain had five stages:
- A graph component loaded the MathLive math library into the main chatgpt.com page, and that library installed a message listener.
- An HTML Preview, running in a sandboxed frame on a separate origin, sent messages to that listener.
- The listener accepted a keyboard-layout update that contained raw HTML.
- That HTML created an iframe without a sandbox attribute, which inherited the chatgpt.com identity.
- A script loaded through a CSP-allowed third-party endpoint executed the final payload.
Each stage looks minor on its own. Together they cross the boundary that's supposed to keep one site's code away from another site's session.
The Sandbox Held, and That's the Real Lesson
Most people hear "sandbox untrusted code" and assume they're covered. Here, the sandbox did its job. Code inside the Preview couldn't read the parent page's DOM. The failure happened one layer over.
postMessage is how frames from different origins talk to each other, and it works like a mail slot. The restricted frame can't walk into the main page, but it can drop a note through the slot. The receiver is responsible for checking who sent the note. In this case, the MathLive listener used a wildcard origin policy and didn't verify that the message came from an approved window before changing state.
When a receiver skips those checks, any frame that can reach it inherits the receiver's privileges. Sandboxing limits what code can do. Origin checks limit what that code can ask trusted code to do. You need both.
Here's where most teams go wrong: they audit their own frames but forget the third-party widgets they load into the same page. A library you added for charts or equations can quietly add a listener you never reviewed.
Why an HTML String Became Script Execution
Setting innerHTML to attacker-controlled markup doesn't run <script> tags, which is why the researchers needed another route. They used an iframe srcdoc element, which carries its full HTML inside an attribute. Because the main page created that frame without a sandbox attribute, the browser gave it the parent's origin. Script inside it could then reach the parent page directly.
The write-up also points to a naming problem. A helper that looked like a sanitizer returned the string marked as trusted, and passed the HTML through unchanged. The name suggested safety; the behavior didn't provide it. Treat any function called sanitize or createHTML as unverified until you've read the code path.
This is where Trusted Types matter. It's a browser API, shipped in Chromium-based browsers, that blocks string-to-sink assignments such as innerHTML unless the value passes through a named policy. Its protection is only as good as the policies you write. A policy that returns its input unchanged is the same bug with better paperwork.
Timing was also part of the exploit. The graph and the Preview loaded independently, so the payload sometimes sent its message before MathLive was ready. The researchers handled this by retrying the same messages for a limited window. Practitioners should take the lesson: a race condition is not a security boundary, and an attacker will simply retry.
The CSP Was Working, and It Still Wasn't Enough
ChatGPT's Content Security Policy blocked inline JavaScript, which is the correct default. It also allowed scripts from YouTube. That allowlist became the final bypass. YouTube's oEmbed endpoint accepted a JSONP-style callback parameter, and it reflected the callback into its response before returning a validation error. Because the policy trusted that domain, the browser ran the response as a script.
Here's the counter-intuitive part: adding a CSP is not the same as having a strong CSP. Host allowlists are easy to ship and easy to bypass whenever an allowed domain hosts JSONP endpoints, redirects, or user-controlled content. Stricter policies built on nonces or 'strict-dynamic', as described in Google's CSP guidance, avoid trusting entire domains. They're harder to retrofit, which is exactly why teams keep the looser version.
The write-up also makes a point about remediation. Blocking only the YouTube endpoint would have removed one path to JavaScript while leaving an untrusted Preview able to insert HTML into the main page. Patching the workaround doesn't fix the root cause.
Why an Alert Box Is the Wrong Measure of Severity
Once the payload was running as chatgpt.com, the impact was no longer about pop-ups. Same-origin requests carry the victim's session cookies automatically. That means the injected script could call ChatGPT's backend APIs as the victim, list recent conversations, and request messages from a chat the attacker had never seen.
Walk through the cause and effect: script runs as chatgpt.com → same-origin requests include the session → the backend returns the victim's data → the script reads the response → a small piece leaves through an ordinary image request to an attacker-controlled server. The researchers sent only a small sample as proof, but the code had access to everything the response contained.
That's why session-level compromise is worse than password theft in some respects. Multi-factor authentication protects the login step. It does little once a session already exists in the browser, because the attacker borrows that session rather than logging in.
Comparing the Defenses: What Each One Actually Stops
No single control closes this chain. The table below compares the main defenses against the specific failure points in the write-up.
| Control | What it blocks | Where it fails | Relative effort |
|---|---|---|---|
Sandboxed iframe without allow-same-origin | Direct DOM access to the parent page | Parent-created frames added without a sandbox attribute | Low |
| postMessage origin and source checks | Untrusted frames driving trusted listeners | Wildcard origins or no check on the sending window | Low |
Strict CSP with nonces or 'strict-dynamic' | Injected and inline script, including via allowed domains | Legacy allowlists kept for compatibility | Medium |
| Trusted Types with narrow policies | String-to-sink assignments such as innerHTML | Policies that return input unchanged | Medium |
| Callback validation before reflection (JSONP removal) | Attacker-chosen code in JSONP responses | Validation that runs after the callback is echoed | Low to medium |
The first two rows would have stopped this chain at its earliest stages. The last two would have blocked the later ones. If you can only do one thing this sprint, audit every postMessage listener in your page. It's the cheapest fix with the largest effect here.
What You Should Do With This
For users, the practical takeaway is to treat shared AI chat links like any other unknown link. If you don't need to open them, don't. If you must, use a separate browser profile or private window where you're not signed in to your main account. This doesn't fix the underlying bug, which OpenAI patched, but it limits what a malicious page can reach.
Check your active sessions in account settings, and sign out of any you don't recognize. If you've opened unfamiliar shared conversations recently and noticed unexpected account activity, change your password and review connected apps.
For builders, run an inventory of third-party scripts and what they listen for. Ship a strict CSP, and treat any allowlisted domain as a potential code-execution surface. Review every innerHTML sink and every frame you create without a sandbox attribute.
OpenAI rolled out the fix across two versions of ChatGPT's web frontend, so the specific chain in this write-up should no longer work. The broader pattern will keep appearing in other products, because it comes from ordinary features that trust each other too much.
Frequently Asked Questions
What is a ChatGPT account takeover?
A ChatGPT account takeover happens when an attacker gains control of a user's signed-in session or data without the user's password. In the Parameter case, a malicious public share ran JavaScript as chatgpt.com in the victim's browser. That code could call authenticated APIs, read private conversations, and send selected data to a server the attacker controlled.
Can a shared ChatGPT link lead to an account takeover?
Yes, in principle, and this research showed it was possible before OpenAI's fix on October 26, 2026. The victim only had to open the link. The chain relied on a trusted page component accepting untrusted messages, not on the victim making a mistake.
How do I protect my ChatGPT account from takeover?
Avoid opening unknown shared AI chat links while signed in. Review and sign out of unfamiliar sessions, use a unique password with multi-factor authentication, and consider a separate browser profile for AI tools. These steps reduce exposure, but the core fix is the platform patch.
Why didn't the sandbox stop this attack?
The sandbox did stop direct access to the main page. The attack went around it by sending messages to a trusted component that didn't verify the sender. Then it used an unsandboxed iframe and a CSP-allowed third-party endpoint to run code with the page's privileges.
Protecting against ChatGPT account takeover comes down to one habit: never assume a sandbox covers everything a page can reach. Check your own frames, listeners, and allowlists this week, and note which one surprises you most. Then pass Parameter's write-up to whoever on your team owns the frontend.