// SECURITY / LIQUID_SECURITY_DOCS
// LIQUID_SECURITY_DOCS
Security vulnerabilities in Shopify themes can expose customer data, enable account takeovers, and compromise entire storefronts. This guide covers every attack vector.
[ 01 ] SECURITY OVERVIEW
Shopify themes execute in a privileged context: they have access to customer session data, cart tokens, and payment flows. A single XSS vulnerability can lead to complete account compromise.
Unlike traditional web apps, Liquid templates are server-rendered — but they frequently inject data into JavaScript contexts, creating dangerous mixing of trusted and untrusted data.
// HIGH RISK PATTERNS
- ⚠ Unescaped Liquid variables in JavaScript contexts
- ⚠ innerHTML assignment with user-controlled data
- ⚠ cart.token exposure in front-end code
- ⚠ eval() with dynamic string construction
- ⚠ document.write() with external data
[ 02 ] XSS IN SHOPIFY THEMES
innerHTML Vulnerabilities
// DANGEROUS: Liquid value injected directly into innerHTML
document.getElementById('product-title').innerHTML = "{{ product.title }}";
// If product.title = '<img src=x onerror=alert(1)>'
// This executes arbitrary JavaScript// SAFE: textContent never executes HTML
document.getElementById('product-title').textContent = "{{ product.title | escape }}";
// Or use the escape filter in Liquid before injection
const title = {{ product.title | json }};
element.textContent = title;Unescaped Output
<script>
var metafield = "{{ product.metafields.custom.description }}";
</script><script>
var metafield = {{ product.metafields.custom.description | json }};
</script>[ 03 ] CSRF VULNERABILITIES
Shopify provides CSRF protection via form tokens, but custom implementations often bypass this protection.
{% form 'customer' %}
{{ form.errors | default_errors }}
<input type="email" name="customer[email]" required>
<button type="submit">Subscribe</button>
{% endform %}
{# form tag automatically includes authenticity_token #}// Include Shopify's CSRF token in fetch requests
const response = await fetch('/cart/add.js', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({ id: variantId, quantity: 1 })
});[ 04 ] SENSITIVE DATA EXPOSURE
cart.token Risks
The cart.token is a session identifier. Exposing it in front-end JavaScript allows session hijacking.
<script>
// NEVER expose cart.token to JavaScript
var cartToken = "{{ cart.token }}";
analytics.track('cart_view', { token: cartToken });
</script><script>
// Use cart API endpoint instead — token is managed server-side
fetch('/cart.js')
.then(r => r.json())
.then(cart => analytics.track('cart_view', { items: cart.item_count }));
</script>[ 05 ] eval() AND DYNAMIC CODE
eval(), new Function(), and setTimeout(string) execute arbitrary code. Combined with Liquid injection, they create critical RCE-equivalent vulnerabilities in the browser.
// CRITICAL: eval() with user-controlled data
var action = "{{ request.path }}";
eval('router.' + action + '()');// EVAL ALTERNATIVES
- →Use object property lookup instead: obj[action]
- →Use switch/case for known values
- →Use JSON.parse() for data — never eval()
- →CSP can block eval() at browser level
[ 06 ] SECURE CODING CHECKLIST
Always escape Liquid output with | escape or | json in JS contexts
REQUIREDNever expose cart.token or customer session data to JavaScript
REQUIREDUse {% form %} tags — never raw HTML forms for Shopify actions
REQUIREDAvoid innerHTML — use textContent or DOM methods
REQUIREDNever use eval(), new Function(), or setTimeout(string)
REQUIREDValidate all URL parameters before using in page logic
REQUIREDImplement Content Security Policy headers
RECOMMENDEDAudit third-party scripts for data collection patterns
RECOMMENDEDUse Syphio to automate security rule enforcement
RECOMMENDED// SEE ALSO
// SYPHIO SCANS ALL OF THIS AUTOMATICALLY