./lock-the-can --start
Lock the Can.
14 checks stand between your app and a raid. Work through each zone, seal what you've handled, and Bandit walks away empty-pawed. Progress saves in your browser.
cd ./front-door
OAuth & install — how merchants let you in.
- CRIT Verify the HMAC on the OAuth callback
Shopify signs the redirect back to your app. Skip the check and Bandit can forge an install and hand you a shop domain he controls.
→ fix: Recompute HMAC-SHA256 over the sorted query string (minus
hmac) with your client secret; reject on mismatch withcrypto.timingSafeEqual. - CRIT Validate the OAuth
statenonceWithout a random, per-request state you have no CSRF protection on install — an attacker can trick a merchant into authorizing under the wrong context.
→ fix: Generate a random
state, store it in a signed cookie/session, and reject the callback if it does not match. - WARN Request the minimum OAuth scopes
Every extra scope is extra loot the day you get breached. `write_orders` you never call is pure downside.
→ fix: List only the scopes each feature needs; re-audit on every release and drop the ones you stopped using.
- WARN Allowlist your OAuth redirect URLs
Open redirects during install can leak the authorization code to an attacker-controlled host.
→ fix: Register exact redirect URIs in the Partner Dashboard; never build them from untrusted input.
cd ./mail-slot
Webhooks — anything the outside world POSTs to you.
- CRIT Verify
X-Shopify-Hmac-Sha256on every webhookWebhook URLs are not secret. Unverified, any POST that looks like Shopify gets trusted — Bandit forges a paid order.
→ fix: Hash the raw request body with your app secret and compare with
timingSafeEqualbefore parsing. See the Dumpster Dive. - WARN Confirm the shop domain matches an install you know
A valid signature only proves it came from Shopify — not that this shop belongs to the session acting on it.
→ fix: Cross-check
X-Shopify-Shop-Domainagainst your stored installs before mutating anything. - INFO Handle the mandatory compliance webhooks
customers/redact, shop/redact, and customers/data_request are required. Ignoring them risks app rejection and privacy violations.
→ fix: Subscribe and implement all three; actually delete/return the data, do not just 200 them.
- WARN Purge tokens on
app/uninstalledA stale access token for an uninstalled shop is a live credential with no owner watching it.
→ fix: On uninstall, revoke/delete the stored token and any cached merchant data.
cd ./the-safe
Secrets & tokens — the stuff worth stealing.
- CRIT Keep access tokens server-side only
A token in localStorage or a client bundle is a permanent Admin credential Bandit lifts with one DevTools glance.
→ fix: Store tokens encrypted at rest server-side; the frontend uses short-lived session tokens via App Bridge.
- WARN Store API secrets in a secret manager, not code
A client secret committed to git is compromised forever, even after you delete the commit.
→ fix: Load secrets from env/secret manager; scan the repo history and rotate anything that leaked.
- CRIT Verify App Bridge session-token JWTs
If your backend trusts a session token without verifying its signature and expiry, it trusts a forgery.
→ fix: Validate the JWT signature with your secret, check
aud,dest, andexpon every request.
cd ./the-perimeter
Embedded UI & merchant input — your walls.
- WARN Set
Content-Security-Policy: frame-ancestorsEmbedded apps must scope who can frame them or you open the door to clickjacking inside the admin.
→ fix: Send
frame-ancestors https://[shop].myshopify.com https://admin.shopify.comper request. - WARN Validate & escape all merchant-supplied input
Metafields, product titles, and form fields are attacker-controllable. Reflect them raw and you have stored XSS.
→ fix: Validate on input, escape on output, and use parameterized queries — never string-build SQL/HTML.
- INFO Respect the API rate limit (handle 429s)
Not security-critical, but a hammered app gets throttled and degrades — and retry storms mask real abuse.
→ fix: Honor the leaky-bucket headers, back off on 429, and queue bulk work.