Your Shopify App Is an API Target

You've built a Shopify app that syncs orders to your fulfillment system. It works. Merchants love it.

Then you get a security disclosure: A penetration tester found that they could enumerate all order IDs by iterating through the API endpoint. They dumped 50,000 customer names and emails without authentication.

This isn't hypothetical. Between 2022–2024, Shopify reported 47 security vulnerabilities in third-party apps. 68% were related to API authentication or input validation—both OWASP Top 10 issues.

The problem: Developers treat Shopify apps as internal tools. But they're internet-facing APIs. Every merchant's data is at stake.

Mapping OWASP Top 10 to Shopify Apps

The OWASP Top 10 is the industry standard for API security. Let's translate it to Shopify:

1. Broken Authentication

Shopify Risk: Your app uses an access token to call the Shopify API. If that token leaks, attackers can read/write all merchant data.

Common Mistake: Storing the token in plaintext. Logging it in error messages. Committing it to GitHub.

Fix: - Store tokens in an encrypted database (use hashing + random salt). - Use environment variables. Never hardcode tokens. - Rotate tokens quarterly. - Use Shopify's latest OAuth 2.0 implementation (not the deprecated OAuth 1.0).

Example (Node.js):

// Bad
const token = "shpat_abc123xyz"; // Hardcoded!
const response = await fetch("https://myshopify.com/admin/api/...", {
  headers: { "X-Shopify-Access-Token": token }
});

// Good
const token = process.env.SHOPIFY_ACCESS_TOKEN;
const encrypted = crypto.encrypt(token, process.env.ENCRYPTION_KEY);
db.saveToken(encrypted);

2. Broken Object Level Access Control (BOLA)

Shopify Risk: Your app exposes an endpoint /api/orders/{id} that returns order data. An attacker increments the order ID (1, 2, 3, 4...) and dumps all orders without authorization.

Why it's critical: Attackers don't need an account. They just iterate through sequential IDs.

Fix: - Always verify the merchant owns the resource before returning it. - Use UUIDs instead of sequential IDs (harder to guess). - Log and alert on suspicious patterns (10+ requests in 1 minute).

Example (Node.js):

// Bad
app.get('/api/orders/:id', (req, res) => {
  const order = db.getOrder(req.params.id);
  res.json(order); // No authorization check!
});

// Good
app.get('/api/orders/:id', authenticate, (req, res) => {
  const order = db.getOrder(req.params.id);
  if (order.shopId !== req.user.shopId) {
    return res.status(403).json({ error: 'Unauthorized' });
  }
  res.json(order);
});

3. Broken Object Property Level Authorization

Shopify Risk: Your app returns order data including profit margin (for internal use). An attacker calls your API and sees profit margins for every order.

Fix: - Never return data you don't intend to expose. - Use allow-lists, not block-lists. Only return fields the user needs.

const safeOrder = {
  id: order.id,
  customer_name: order.customer_name,
  total: order.total,
  // Don't include: profit_margin, supplier_cost, internal_notes
};

4. Unrestricted Resource Consumption

Shopify Risk: An attacker hits your API with 10,000 requests per second. Your database melts. The service is down.

Fix: - Implement rate limiting. Shopify API allows 2 requests/second. Your app should allow less. - Set timeouts on database queries. - Monitor for abuse patterns.

const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 30 // 30 requests per minute
});
app.use('/api/', limiter);

5. Broken Function Level Access Control

Shopify Risk: Your app has an endpoint /api/admin/users that creates new admin accounts. An attacker discovers it and creates backdoor accounts.

Fix: - Scope API endpoints by user role. - Require explicit permission checks. - Never expose admin endpoints to end users.

6. Unrestricted Access to Sensitive Business Flows

Shopify Risk: Your app allows mass order creation via an API endpoint. An attacker creates 50,000 fake orders, crashes your fulfillment system.

Fix: - Require multi-factor confirmation for sensitive operations. - Log all sensitive actions with audit trails. - Implement CAPTCHA or step-up authentication for bulk operations.

7. Server-Side Template Injection (SSTI)

Shopify Risk: Your app takes a merchant's email template and renders it with Liquid. An attacker injects Liquid code that leaks data or executes commands.

Fix: - Sanitize all user input before templating. - Use a sandboxed template engine. - Never pass untrusted data to Liquid without escaping.

// Bad
const template = req.body.emailTemplate; // User input!
const rendered = Liquid.render(template, data);

// Good
const template = db.getTemplate(req.body.templateId); // Pre-approved template
const rendered = Liquid.render(template, { /* safe data */ });

8. Broken Cryptography

Shopify Risk: You store customer credit card data using weak encryption (ROT13, XOR). An attacker decrypts it trivially.

Fix: - Use AES-256 for encryption. - Use bcrypt or Argon2 for password hashing. - Never store card data yourself—use Shopify's Payment API.

9. Improper Inventory & Asset Management

Shopify Risk: You deploy your app to production but leave debug mode enabled. Error messages leak environment variables and API keys.

Fix: - Disable debug mode in production. - Use secure logging (no tokens, keys, or PII in logs). - Regularly audit dependencies for known vulnerabilities.

npm audit
npm update

10. Insufficient Logging & Monitoring

Shopify Risk: An attacker compromises your app. You don't notice for 6 months because you're not logging API calls.

Fix: - Log all authentication attempts. - Log all data access (who accessed what, when). - Set up alerts for anomalies (10x normal request rate, failed logins >5, etc.).

Shopify-Specific Security Patterns

Pattern 1: Webhook Verification

Shopify sends webhooks to your app. An attacker forges webhooks to your endpoint. You process fake order data.

// Verify the webhook is from Shopify
const crypto = require('crypto');

function verifyWebhook(req, secret) {
  const hmac = req.headers['x-shopify-hmac-sha256'];
  const body = req.rawBody; // Raw request body, not parsed JSON

  const calculated = crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('base64');

  return calculated === hmac;
}

app.post('/webhooks/orders/create', (req, res) => {
  if (!verifyWebhook(req, process.env.SHOPIFY_API_SECRET)) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  // Process the webhook
});

Pattern 2: Scoped Access Tokens

When a merchant installs your app, request only the scopes you need. More scopes = more risk if compromised.

Recommended minimal scopes: - read_products — read product data - write_orders — create orders - read_customers — read customer data (only if necessary)

Never request admin scope unless absolutely required.

Pattern 3: Regular Penetration Testing

For production apps with >10,000 merchants, conduct annual penetration tests. The cost ($5K–$15K) is cheap compared to a breach ($100K+ in liability, reputation damage).

Security Checklist for Production Apps

  • [ ] Tokens stored encrypted, never in plaintext or logs
  • [ ] All endpoints require authentication
  • [ ] All endpoints verify merchant ownership (BOLA prevention)
  • [ ] Rate limiting enabled (max 30 req/min per merchant)
  • [ ] Webhooks verified with HMAC
  • [ ] Input validation on all endpoints
  • [ ] No debug mode in production
  • [ ] Audit logging for all data access
  • [ ] Dependencies scanned for vulnerabilities (npm audit)
  • [ ] Error messages don't leak sensitive information
  • [ ] Sensitive operations logged and monitored
  • [ ] HTTPS enforced for all API calls
  • [ ] Regular security reviews (quarterly)
  • [ ] Incident response plan written and tested

The Cost of Security Debt

A 2024 security breach affecting Shopify apps cost one developer agency $500K in liability, lost customers, and reputation damage. The app had basic BOLA vulnerabilities.

Shopify's bug bounty program pays $500–$5,000 for reported vulnerabilities. If a researcher finds your OWASP flaws, you'd rather they report it to you than to Shopify's program—or worse, to an attacker.


Ready to Secure Your Shopify Infrastructure?

Security isn't a feature. It's the foundation. The best Shopify apps balance speed and security—never speed at security's expense.

Tenten conducts security audits for Shopify Plus apps and custom integrations. We map your infrastructure against OWASP, identify vulnerabilities, and recommend fixes. Contact us for a security assessment, or explore Shopify development best practices.


Editorial Note

OWASP Top 10 vulnerabilities exist in 70% of production apps. Not because developers are careless. Because security feels like overhead until you get compromised.

Frequently Asked Questions

What's the difference between authentication and authorization?

Authentication = verifying who you are (login). Authorization = verifying what you can access (permissions). You need both.

Should I implement API keys or OAuth 2.0?

OAuth 2.0. It's the modern standard. API keys are simpler but less secure for merchant data.

How often should I rotate Shopify access tokens?

Quarterly for sensitive apps. Immediately if compromised. For low-risk integrations, annually is acceptable.

What's the risk of exposing order IDs sequentially?

An attacker can enumerate all orders without auth. This violates BOLA (Broken Object Level Access). Fix it by using UUIDs instead.

Do I need to conduct a penetration test?

For apps with <1,000 merchants, a self-audit suffices. For 10,000+ merchants, annual professional testing is standard.

What should I log for security audits?

Authentication attempts (success/fail), data access (who, what, when), API calls (method, endpoint, status), and errors.

Can I store customer credit card data in my app?

No. Use Shopify's Payment API. Storing cards yourself creates PCI compliance liability ($25K+ per violation).