Skip to content
BuildThenTestedSee packages

Expo security: defaults, what breaks, what to check

Expo is a React Native framework for Android, iOS and web apps, with EAS services for builds, updates and hosting. Below: what it configures by default according to its own documentation (verified 2026-09-21), the 15 patterns most associated with it, the security features the vendor offers and where they stop, and a checklist you can run without buying anything.

The Expo docs treat everything shipped in the client bundle as public; secret handling belongs in API routes or another server.

Expo is a React Native framework for Android, iOS and web, with EAS for builds, updates and environment variables. Variables prefixed EXPO_PUBLIC_ are inlined into the app bundle; everything in the bundle can be read from the installed app.

Mobile apps add three questions to the web list. What is inside the shipped bundle. Whether network traffic is pinned to your servers where it matters. What happens to local data when the device is offline and comes back. The patterns here cover those plus the backend's own rules.

Expo defaults that matter for testing

Each row is taken from the vendor's documentation on the date shown. Defaults change; the date is the claim.

Defaults of Expo as documented by the vendor
AreaDefaultSource
EXPO_PUBLIC_ env exposureExpo CLI loads EXPO_PUBLIC_ variables from .env files and Metro inlines them into the client JavaScript bundle. The docs say not to store private keys in them because they are visible in plain text in the compiled app.docs · verified 2026-09-21
Secrets in bundleEAS secret-type variables are not readable outside EAS servers, but the docs state they provide no additional security for values you embed in the app; anything in client-side code should be considered public.docs · verified 2026-09-21
EAS variable visibility levelsPlain text (visible everywhere), Sensitive (obfuscated in logs, toggle to view on the website) and Secret (not readable outside EAS servers); available to EAS Build, Workflows, Update and Hosting.docs · verified 2026-09-21
Server-side codeExpo Router API routes run on a server, isolated from client code, and can read all environment variables; they require web.output server (or static with the apiRoutes plugin) and a host such as EAS Hosting.docs · verified 2026-09-21
On-device storageexpo-secure-store encrypts key-value pairs with Android Keystore and iOS keychain; not available on web, and some iOS releases rejected values above roughly 2048 bytes.docs · verified 2026-09-21

Documentation home: docs.expo.dev

What breaks in Expo projects

15 patterns in the catalogue name Expo; the most severe are listed here across secrets and keys, data access, login and permissions, ai features, mobile, payments, reliability, deploy and config, performance. Attribution is descriptive: the default stack makes the pattern reachable, or a public incident names it.

  • Secrets committed to the code repositorySecrets and keys

    Everyone who can see the repository, now or later, holds working keys to the database, payment account and model provider, and the keys stay in history after the file is deleted.

    Critical
  • Record ids accepted without an ownership checkData access

    Anyone who changes the id in a URL or request can read, edit or delete another customer's record.

    Critical
  • Authorization enforced only in the browserLogin and permissions

    Anyone who calls the API directly, or edits the page in developer tools, gets the data and actions the screen was hiding.

    Critical
  • LLM API key exposed to the clientAI features

    Anyone who views the page source can copy the key and run their own workloads on your account until the bill or the provider stops them.

    Critical
  • Secrets in the mobile app bundleMobile

    Anyone who downloads the app from the store can unzip it and read the key, then run up your bill or read your database as an administrator.

    Critical
  • Third-party API key in the JavaScript bundleSecrets and keys

    Anyone who views the page source can take the key and run up charges, send messages or pull data on your account until the key is revoked.

    High
  • Subscription state trusted from local storagePayments

    Anyone can type one line in the browser console to become a paying customer, and the server hands over the paid feature without checking.

    High
  • No rate limiting on sensitive routesReliability

    A script can try thousands of passwords against your login form, hammer a form that sends emails or calls a paid API, or knock the app over, and the bill lands on you.

    High
  • Dependencies with known vulnerabilitiesDeploy and config

    A published advisory is a public instruction sheet, so an attacker does not need to find a flaw in your code; they only need to know which library version you run.

    High
  • Offline state corrupting dataMobile

    Users lose edits they watched themselves save, records end up half-updated, and nobody can tell which copy of the data is the real one.

    High
  • Errors swallowed, spinner never stopsReliability

    When a request fails, users see a spinner that never ends or an empty list, assume the app is broken or their data is gone, and nobody on your side hears about it.

    Medium
  • Retries with no pause and no capReliability

    One outage turns into a flood of retries from every open browser tab, which keeps the service down longer, runs up usage bills and can submit the same order or payment twice.

    Medium
  • List endpoints that return everythingPerformance

    As the table grows, the response gets slower and heavier until it hits a platform body-size or time limit and the page simply stops loading, usually for your biggest customer first.

    Medium
  • Whole table downloaded, filtered in the browserPerformance

    Every user's device downloads far more than it shows, so pages slow down as the table grows, and if the database does not also restrict rows, other people's records land in the browser.

    Medium
  • Certificate pinning absentMobile

    Someone who can put a certificate on a phone, for example on a shared or managed device or a rooted one, can read and alter everything the app sends, including login tokens.

    Medium

Public incidents

Lovable reported that from 3 February to 20 April 2026 a backend regression let any logged-in Lovable user read the chat history and source code of public projects; a fix shipped within two hours.

  • 2026-02-03 to 2026-04-20 Exposure window
  • 2026-02-22 First HackerOne report
  • about 2 hours Time from public report to fix

Apr 2026 · Lovable · all sources

One education app built on Lovable had 16 vulnerabilities, 6 critical; a database function denied callers whose role was 'authenticated', so anonymous callers passed and 18,697 user records were readable without login.

  • 16 Vulnerabilities found
  • 6 Critical
  • 18,697 User records exposed
  • 14,928 Unique email addresses
  • 870 Enterprise users with full PII

Feb 2026 · Taimur Khan · all sources

On the Base44 app builder, undocumented registration and one-time-password endpoints accepted a non-secret app_id, so anyone could create a verified account on a private app; Wix fixed it within 24 hours.

  • 2025-07-09 Reported to Wix
  • 2025-07-10 Fix verified
  • under 24 hours Time to fix
  • 2025-07-29 Public disclosure

Jul 2025 · Wiz Research · all sources

Two days after announcing a SaaS built with Cursor and no hand-written code, its founder reported subscription bypasses, maxed-out API keys and unwanted database writes; he shut the app down three days later.

  • 2025-03-15 Launch announcement
  • 2025-03-17 Report of subscription bypass and API key abuse
  • 2025-03-20 Shutdown announcement

Mar 2025 · Leo (Enrichlead founder) · all sources

Each entry links to its primary source in the evidence list. What to do about it is the self-check below.

Expo's own security features, and where they stop

Stated from the vendor's documentation. The limits are the vendor's scope statements, not judgements.

  • EAS secret environment variables

    Secret visibility keeps values off the website, EAS CLI and job logs, for use during builds and workflows.

    Where it stops: The docs state secrets do not protect values that end up embedded in the application itself.

    vendor docs · verified 2026-09-21

  • API routes

    Sensitive data in +api.ts files is excluded from the client bundle even if imported elsewhere in the app directory; unsupported methods return 405.

    Where it stops: Production use requires deploying the server (EAS Hosting or a third party); the route's own authorization is the app's code.

    vendor docs · verified 2026-09-21

  • expo-secure-store

    Encrypted on-device storage backed by Keystore and keychain, with an optional biometric requireAuthentication flag.

    Where it stops: Data protected with requireAuthentication becomes inaccessible after biometric changes; Android entries are lost on uninstall and cannot be restored from Auto Backup.

    vendor docs · verified 2026-09-21

Check it yourself, in this order

The critical patterns first. Each uses the dashboard and your browser.

Secrets committed to the code repository

  1. Open your project on GitHub (or the code view in your builder) and look at the file list for .env, .env.local, .env.production, serviceAccountKey.json or firebase-adminsdk files.
  2. Use the repository search box and search for sk_live, sk-, service_role, BEGIN PRIVATE KEY and password=.
  3. If you have the code on your computer, run one command in the project folder: git log --all -p -S sk_live -- . | head. Any output means a live key has been committed at some point.
  4. On GitHub, open Settings, then Code security, and check whether Secret scanning shows alerts.
  5. A bad result is any env or key file in the file list, any search hit that shows a real key, or an open secret scanning alert.

Record ids accepted without an ownership check

  1. Create two accounts in your app and add one record, such as an order or a note, in each.
  2. In account A, open a record and copy its address from the browser bar, or open developer tools (right-click, Inspect, then Network) and copy the request that loads it.
  3. Sign in as account B in a private window and paste that address, or use Copy as cURL on the request and run it with account B's cookies.
  4. Try the same with an id one higher or lower than your own, and with the edit and delete actions.

A bad result is account A's record appearing for account B, or an edit or delete going through.

Authorization enforced only in the browser

  1. Sign in as a normal user and open developer tools (right-click, Inspect), then the Network tab.
  2. Browse the app and note any request whose address contains admin, users, export or all.
  3. Right-click one of those requests, choose Copy as cURL and run it in a terminal, or open its address in a new tab.
  4. Change the address to a path you were never shown, such as /api/admin/users, and load it while signed in as the normal user.

A bad result is a full response where you expected an HTTP 401 or HTTP 403 error.

Expo pre-launch checklist

15 checks, in the order we run them. Usable without buying anything.

Self-check tool

Answer questions about your setup and get the patterns that apply, ordered by severity. Runs in your browser; nothing is sent.

Run the self-check

Packages for Expo projects

Pre-launch check

Start here

For founders about to let real users in.

$490

Report in 48 hours
  • Keys and secrets in the browser or repo
  • Who can read and change whose data
  • Login, roles and admin routes
  • Errors, retries and rate limits
  • Debug mode, headers, deploy path

What you receive

Full audit

For teams and startups who need a document, not just a list.

$1,900

Report in 5 business days
  • All catalogue categories
  • Repository and live app
  • Investor-ready summary document
  • Prioritised fix roadmap
  • One-hour walkthrough call

What you receive

All packages and prices. Every report includes a re-check after your fixes.

Questions about Expo

expo app security checklist

No secrets in EXPO_PUBLIC_ variables or code, secrets read from a server you control, backend rules tested with two accounts, certificate pinning where the data warrants it, offline writes that reconcile safely, and dependency updates. Each item maps to a pattern on this page.

expo_public env variable exposed

Expo's docs state EXPO_PUBLIC_ variables are embedded in the bundle. Anything with that prefix is readable from the installed app; treat it as public and move secrets behind your own API.

react native app security audit

We unpack the built app to read what ships, test the backend it talks to with two accounts, check transport settings, and test offline behaviour on a device. Findings include the file or setting to change.