Apply Shopify security best practices for API credentials, webhook HMAC validation, and access scope management. Use when securing API keys, validating webhook signatures, or auditing Shopify security configuration. Trigger with phrases like "shopify security", "shopify secrets", "secure shopify", "shopify HMAC", "shopify webhook verify".
Security essentials for Shopify apps: credential management, webhook HMAC validation, request verification, and least-privilege access scopes.
# .env — NEVER commit
SHOPIFY_API_KEY=your_api_key
SHOPIFY_API_SECRET=your_api_secret_key
SHOPIFY_ACCESS_TOKEN=shpat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# .gitignore — add immediately
.env
.env.local
.env.*.local
*.pem
Token format reference:
| Token Type | Prefix | Length |
|---|
| Used For |
|---|
| Admin API access token | shpat_ | 38 chars | Server-side Admin API |
| Storefront API token | varies | varies | Client-safe storefront queries |
| API secret key | none | 32+ hex | Webhook HMAC, OAuth |
Shopify signs every webhook with your app's API secret using HMAC-SHA256. The signature is in the X-Shopify-Hmac-Sha256 header. Use crypto.timingSafeEqual for comparison to prevent timing attacks. The middleware must use raw body parser (not JSON parser).
See Webhook HMAC Verification for the complete implementation.
Verify that incoming OAuth requests from Shopify are authentic by checking the HMAC query parameter. The library handles this automatically, but the manual approach sorts params alphabetically, creates a query string, and compares HMAC hex digests.
See OAuth Request Verification for the complete implementation.
Only request the scopes your app actually needs:
| Use Case | Required Scopes |
|---|---|
| Read-only product catalog | read_products |
| Product management | read_products, write_products |
| Order dashboard | read_orders |
| Fulfillment automation | read_orders, write_fulfillments, read_fulfillments |
| Customer loyalty app | read_customers, write_customers |
| Full admin app | Request scopes incrementally, not all at once |
# shopify.app.toml — start minimal, add as needed
[access_scopes]
scopes = "read_products"
# Use optional scopes for features that not all merchants need
[access_scopes.optional]
scopes = "write_products,read_orders"
// Embedded apps must set proper CSP headers
app.use((req, res, next) => {
const shop = req.query.shop as string;
res.setHeader(
"Content-Security-Policy",
`frame-ancestors https://${shop} https://admin.shopify.com;`
);
next();
});
| Security Issue | Detection | Mitigation |
|---|---|---|
| Token in git history | git log -p | grep shpat_ | Rotate token immediately, use git-secrets |
| Invalid webhook HMAC | 401 responses in webhook handler | Verify API secret matches Partner Dashboard |
| Missing scope | 403 errors on API calls | Add scope to shopify.app.toml and re-auth |
| Token exposed in client JS | Browser devtools | Never send admin tokens to the browser |
.env files in .gitignoreframe-ancestors set for embedded appsgit-secrets or similar pre-commit hook installed# Install git-secrets
brew install git-secrets # macOS
# or: sudo apt install git-secrets # Linux
# Add Shopify patterns
git secrets --add 'shpat_[a-f0-9]{32}'
git secrets --add 'shpss_[a-f0-9]{32}'
# Install hook
git secrets --install