Skip to content

[Developer security] Don't send account data to untrusted tabs - #9056

Open
DNin01 wants to merge 3 commits into
ScratchAddons:masterfrom
DNin01:localhost-security
Open

[Developer security] Don't send account data to untrusted tabs#9056
DNin01 wants to merge 3 commits into
ScratchAddons:masterfrom
DNin01:localhost-security

Conversation

@DNin01

@DNin01 DNin01 commented Jun 3, 2026

Copy link
Copy Markdown
Member

Resolves #9054

Changes

Previously, Scratch Addons could hand out your CSRF token and X-Token to tabs of sites outside scratch.mit.edu, including localhost, where the content script exposed them via the window object, potentially putting developers' Scratch accounts at risk.

Now, account info and credentials are replaced with a placeholder before the data is sent anywhere except scratch.mit.edu, preventing unauthorized webpages from seeing your account data.

This change applies to two background modules, get-userscripts.js and global-state.js.

The different data received by two tabs on different URLs is shown below:

globalState auth on scratch globalState auth on localhost

The two objects would have been the same before this change.

Reason for changes

These changes introduce a valuable security measure for developers. While Scratch accounts aren't as important as others, they can still be used for impersonation if compromised. They're still meaningful assets that deserve protection.

These fixes shouldn't affect the developer experience. Logged-out user status is already handled gracefully by the extension, so nothing will fail. Production account credentials were never meant to be used by local instances of scratch-www anyway.

See #9052 for more info.

Security overview

I would rate this security issue a "moderate" due to the specific conditions an attacker would need to exploit it.

I would say a full security advisory isn't necessary for this vulnerability due to the limited impact (likely a few hundred developers who actively use recent versions of the source code) and low probability of exploitation.

For all Scratch Addons developers, I would recommend disabling http://localhost/* in the Manage Extension menu when you're not actively testing scratch-www locally. This prevents exploits like the one described above.

Notes

Tested in Edge 148.

Changes inspired by a conversation with GitHub Copilot (Claude Haiku 4.5).

DNin01 added 3 commits June 2, 2026 16:26
This ensures content scripts only get your tokens when injected into a trusted location
So the files can share the same logic
@DNin01
DNin01 requested a review from WorldLanguages as a code owner June 3, 2026 02:41
@DNin01 DNin01 added type: bug A bug in the addon loader, or in a specific addon scope: core Related to the core script/extension workings labels Jun 3, 2026
@WorldLanguages

Copy link
Copy Markdown
Member

Production account credentials were never meant to be used by local instances of scratch-www anyway.

I'm not sure about this. Can anyone confirm?

I thought the usual way of launching scratch-www was by using api.scratch.mit.edu which would require a real X-Token to work

The CSRF token is useless in scratch-www so no problem there

@WorldLanguages

WorldLanguages commented Jun 3, 2026

Copy link
Copy Markdown
Member

Also, worth checking if the content script(s) ever actually read from globalState.XToken. I'm not sure about that, since it depends on how we implemented support for incognito and Firefox containers, so I don't remember right now. (remember that there's a single globalState but the user might be logged in into different accounts based on the specific tab due to incognito or FF containers)

If that property is never read, no need for complicated fixes, we can just remove it from globalState

@DNin01

DNin01 commented Jun 3, 2026

Copy link
Copy Markdown
Member Author

Also, worth checking if the content script(s) ever actually read from globalState.XToken.

I also don't see why a content script wouldn't be capable of getting any token by itself, without a background context or secure message passing headaches. Could look into that in the future.

@mxmou

mxmou commented Jun 3, 2026

Copy link
Copy Markdown
Member

I also don't see why a content script wouldn't be capable of getting any token by itself, without a background context or secure message passing headaches. Could look into that in the future.

The x-token could be read from the Redux state. In an addon userscript, the following would work: addon.tab.redux.state.session.session.user.token

animated-thumb already reads the CSRF token from document.cookie instead of using addon.auth:

getCSRFToken() {
const tokens = /scratchcsrftoken=([\w]+)/.exec(document.cookie);
return tokens[1];
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: core Related to the core script/extension workings type: bug A bug in the addon loader, or in a specific addon type: security Security issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tokens should not be shared with localhost

4 participants