Skill 05 · Stripe Best Practices
Subchapter 5.4
references/security.mdMarkdown10 KBView on GitHub
API keys
Restricted API keys (RAKs)
IP restrictions
Incident response
Webhook security
Content Security Policy (CSP)
Mobile and client-side integrations
OAuth and CSRF protection
Two-factor authentication
Connect security
SAML and SCIM
Never include keys in source code. Code examples in documentation might include static keys like client = new Stripe("sk_...") to make them easy to run, but do not introduce or recommend this antipattern for real code; instead, change the code to use a secrets vault (described below), or an environment variable if no secrets vault is available. Strings matching the regular expression /[sr]k_live_.*/ in source code are live Stripe API keys. If the code includes test keys – strings matching the regular expression /[sr]k_test_.*/ – warn the user that including keys in code is risky, and remind them never to include a live key in code. Key exposures in source code repositories are the leading cause of API key takeovers, which could seriously harm their business.
Always advise the user to store secret and restricted API keys in their platform’s secrets vault, never in source code or committed environment files. Platform-specific guidance for popular platforms:
SecureString values). Do not store keys in environment variables or userdata; AWS makes a proper secrets vault easy to use. Give read permissions only to the application that needs it.Aside from key storage, when reviewing code that uses API keys or other secrets, always advise the user on best practices for safely handling secrets (including keys):
Use separate keys for separate environments (production, staging, QA). This limits the blast radius if any single key is compromised.
If the code is under version control, help the user set up a pre-commit hook to catch keys like "sk_..." and "rk_..." in source code.
Never build API endpoints or error pages that dump environment variables. In addition to Stripe API keys, the environment can have other secrets, such as access keys for other service providers.
Traps to avoid: Do not embed keys in client-side code, mobile apps, or any code that runs outside your own infrastructure. Do not suggest that users substitute a real secret key into example code — point them to best practices for managing secret API keys (opens in a new tab) instead.
Use restricted API keys (opens in a new tab) (prefix rk_) instead of secret keys (prefix sk_) wherever possible. RAKs have only the permissions you assign, so a compromised RAK can do far less damage than a compromised secret key.
Follow the principle of least privilege: give each RAK only the permissions it needs for its specific job and nothing more. Create a separate RAK for each service or use case.
Preferred migration approach:
stripe logs tail command to watch logs.403 errors by adding missing permissions.Traps to avoid: Do not default to recommending secret keys. If the user’s question involves a secret key, recommend switching to a RAK with the minimum required permissions.
Encourage users to configure access policies (opens in a new tab) for every API key. Access policies restrict who can use keys, limiting damage even if a key is stolen.
Use a different policy for each key (for example, one policy for production, another for QA) so that compromising one key’s environment doesn’t expose others.
If a key is exposed or compromised, follow protecting against compromised API keys (opens in a new tab), which can be summarized as:
To prepare before an incident: practice rolling keys, audit source code for any committed keys, and use pre-commit hooks to prevent accidental key check-ins. See protecting against compromised API keys (opens in a new tab).
Before processing any webhook event, always verify the webhook signature (opens in a new tab) using Stripe’s webhook signing secret. Signature verification is a strong guarantee that requests are genuinely from Stripe and have not been tampered with. Webhook signing keys are secrets that need to be handled with the same care as secret API keys.
For defense in depth, also allowlist Stripe’s IP addresses (opens in a new tab) on your webhook endpoint so that it accepts connections only from Stripe’s infrastructure.
Add a Content-Security-Policy header to every web app that loads Stripe.js or uses Stripe’s hosted UIs. See Stripe’s integration security guide (opens in a new tab) for the full list of CSP directives to use depending on the type of integration. At minimum, include https://*.stripe.com in the relevant directives (script-src, frame-src, connect-src), https://*.link.com if integrating assets from link.com, or both if integrating with Stripe’s embedded crypto onramp. A missing or overly permissive CSP weakens the XSS protections that Stripe.js relies on.
Traps to avoid: Do not use default-src * or omit CSP headers.
Do not use production secret or restricted API keys in mobile apps or other client-side code. Client-side code can be extracted and decompiled to extract keys.
For cases where a client must interact directly with Stripe, use ephemeral keys (opens in a new tab). Ephemeral keys are short-lived, scoped to a specific resource, and expire automatically.
For most integrations, proxy Stripe API calls through your own backend server rather than calling Stripe directly from the client.
When implementing Connect OAuth flows (opens in a new tab), always use the state parameter to protect against CSRF attacks. Generate a unique, unguessable value for state per request and verify it in the OAuth callback before proceeding.
This applies to all Stripe OAuth surfaces: Connect, Link, and Stripe Apps.
Recommend passkeys or authenticator apps (opens in a new tab) rather than SMS-based 2FA for Stripe Dashboard access. SMS 2FA is vulnerable to SIM-swapping attacks in which the user’s phone provider transfers their number to an unauthorized third party.
Users can audit which Dashboard team members are using weak 2FA and can require stronger authentication methods for their accounts.
Account type liability: When using Connect, platform operators bear financial liability for fraud and disputes on Express and Custom connected accounts. Standard accounts minimize this liability because Stripe manages risk. Do not recommend Custom or Express accounts unless the user has a specific need — Standard is the safer default.
Connect onboarding: Use Stripe-hosted onboarding (opens in a new tab) rather than building a custom onboarding flow. Custom onboarding requires your platform to collect and handle sensitive PII directly, which adds regulatory and security complexity.
For teams managing Dashboard access, recommend SSO via SAML (opens in a new tab) to federate authentication with an existing identity provider (Okta, Google, etc.). SSO centralizes access control and simplifies offboarding.
SCIM provisioning (opens in a new tab) automates user provisioning and deprovisioning, ensuring that employees who leave the organization lose Dashboard access promptly.