A web page can forge messages to a content script, so the extension must never treat page input as trusted. The safest design has content scripts send small parameters, such as an item ID, and lets the privileged part of the extension build the request and validate everything before acting.
Where the trust boundary sits
A content script lives next to an untrusted page. Google's content script documentation says they share the DOM but not JavaScript state, and that to talk to the page they use the shared DOM, for example with window.postMessage. Anything on that channel could come from the site's own scripts, a third-party script on the site, or an attacker who found a script injection bug.
The service worker, by contrast, holds the extension's real power. Content scripts can only reach other extension APIs by messaging it. That makes the message handler the gate.
The forging problem
Google's network request guidance spells out the risk: do not let content scripts request arbitrary URLs, because a malicious page could forge messages and use the extension to reach cross-origin resources. Host permissions let the service worker fetch from other origins, so a handler that takes a URL from a message becomes a proxy for the attacker.
Design rules
- Send parameters, not URLs. A message like fetch item 1234 is safe. A message like fetch this address is not. Build the URL in the service worker from a fixed base.
- Validate type and range. Check that the ID is the expected type and falls in a known pattern.
- Keep handlers narrow. One handler per action. Avoid a generic "run this" message.
- Use HTTPS only. Google notes HTTP resources can be modified by a network attacker.
- Never inject fetched content as HTML. Use textContent, not innerHTML, as the guidance advises.
- Filter what you pass to the page. If a content script receives content from another website, filter it for cross-site scripting before injecting it.
- Avoid eval. Use JSON.parse, and pass functions, not strings, to setTimeout.
A quick review method
For each message handler in an extension, write down three things: who can send this message, what the handler does with each field, and what is the worst thing an attacker could trigger. If the worst case is bad, reduce the handler's power or add a user confirmation step.
Page-to-extension messaging through the manifest is a related surface. See externally_connectable for the extension's declared list of pages and extensions that may message it.
Common mistakes
A frequent one is a generic message type that takes a function name or a URL and runs it. It is flexible for the developer and equally flexible for an attacker. Replace it with one narrow message per action, and write a test that sends malformed values to each handler.
Quick check
Is it safe to pass a URL in a message to the service worker?
Not if the content script got it from the page. Pass an identifier and build the URL inside the extension.
Should content scripts use innerHTML for fetched data?
Google advises textContent instead, since it does not let an attacker inject HTML elements.
Sources
Last reviewed: 19 October 2026.