Render untrusted content as text
Render every value that you didn't write yourself as text, using an API that can't produce markup.
Cross-site scripting (XSS) happens when content that an attacker controls is rendered as markup or code. The content can arrive through any field your app renders: a product title or description, a variant value, a metafield, a cart line item property, a discount code, a search query, an uploaded file name, or any setting that you don't control. A script that runs this way can read sessions, tokens, and the personal and payment information entered on the page, act as a logged-in merchant in the Shopify admin, or change a storefront.
Shopify serves a Content Security Policy (CSP) that limits what a browser loads. It's permissive enough for apps and themes to work, and it doesn't restrict markup that your own code renders into the page, so it's a backstop rather than a control.
Search your code for the sinks that execute untrusted input, and replace them:
textContent covers text. Handle code, HTML input, and URLs separately:
- Never execute as code a string supplied by a store owner, a user, or any other source outside your control. Configuration and customization have better answers: structured settings, a fixed set of options, or a sandboxed extension point.
- If your product needs to accept HTML, then sanitize it on your server with a well-maintained sanitizer and a strict allowlist of tags and attributes.
- Validate URLs. Allow only the schemes you need, which is normally just
https:, and rejectjavascript:, unexpecteddata:URLs, and protocol-relative URLs.
For the Liquid side of this practice, see Escape every value for its context.