You uploaded your build to App Store Connect last week, and the same uneasy thought keeps coming back: Apple has the IPA, the reviewer has run it, and somewhere inside the build are the environment variables you set during your last archive. Does the review team see them? The honest answer is more nuanced than yes or no.
Short answer
Apple's App Review process does not specifically grep your iOS binary for environment variables or .env files. Review looks at runtime behavior, privacy declarations, and policy compliance, not at the contents of static string tables in your Mach-O executable. The deeper problem is that values you injected through environment variables at build time often end up as plain text inside the compiled binary anyway, fully readable by the strings command, as documented by NSHipster's analysis of secret management in iOS. Apple does not need to flag them, because anyone who downloads your app from the App Store can.
What you should know
- Environment variables in Xcode are a scheme setting, not a packaging mechanism. Variables set in the Run scheme are only present when launching from Xcode, per Apple's Xcode environment variable reference. They do not ship to TestFlight or the App Store.
- Values baked into
Info.plist,xcconfig, or generated Swift files during build do ship. They are stored as readable strings inside the IPA bundle. - App Review does not publish a check that scans for hardcoded credentials. No part of the App Review Guidelines names this behavior explicitly.
- OWASP MASVS treats hardcoded API keys as a Storage weakness, not a review concern. See MASWE-0005 in the OWASP MAS project for the formal classification.
- The risk is exposure after the app ships, not rejection during review. Attackers download the IPA, run
strings, and pull keys in seconds.
Does App Review actually look at environment variables in your build?
The short answer is no, not the way a security scanner does. Apple's review team is concerned with whether your app behaves the way your metadata and Privacy Manifest describe it. Reviewers run the binary, look for crashes, check that Sign in with Apple is present when you offer other social logins, confirm your IAP flow uses StoreKit, and verify that any data collection matches your App Store Connect privacy answers. There is no public step in that process that says, extract embedded credentials and check whether you exposed them.
That does not mean Apple is blind to bundled content. The static analysis layer that runs during build upload looks at API symbol usage, framework dependencies, and required reason declarations. If you call a Required Reason API without declaring it in your Privacy Manifest, the upload step will surface an ITMS warning, and your build may be blocked, per Apple's required reason API documentation. That warning is about API call patterns, not about strings in the binary that happen to look like AWS keys or Stripe tokens.
If you submitted an app with an exposed OpenAI key, App Review will most likely not call it out. The build goes through, your app reaches the store, and the credential is readable by anyone who downloads the IPA from the App Store and unpacks it.
Where do build-time env variables actually end up after Xcode compiles?
Three places, depending on how you wired them in. Each behaves differently after the build is sealed.
| Source of env variable | Where it ends up | Readable by strings on IPA? |
|---|---|---|
| Xcode Run scheme environment | In memory only during Xcode debug runs | No, not present in shipped build |
xcconfig file referenced by build settings | Compiled into Info.plist or as preprocessor macros | Yes |
| Generated Swift file (GYB or build script) | Compiled into the Mach-O string table | Yes, often as raw string literal |
.env file copied into bundle resources | Sits as plain text in the app bundle | Yes, trivially readable |
| Fetched from backend at runtime over HTTPS | Never touches the binary | No |
The pattern is consistent: anything injected during build and referenced by your shipping code lands as a readable string inside the executable. The Run scheme environment, set in the Xcode scheme editor, is the rare safe case, because it only applies when the debugger launches the app on a connected device or simulator. It does not get archived into the .xcarchive that becomes your IPA, as described in Appcircle's guide to environment variables in iOS projects.
This distinction trips up a lot of teams that started in web or backend work, where process.env.OPENAI_API_KEY reads from a hosted environment that never reaches the client. In iOS, the closest analogue is a value you fetch from your own backend at runtime, not a value bundled with the app.
What happens to a .env file shipped with an iOS app?
A .env file shipped inside the app bundle is the highest-risk pattern in this space. The file is plain UTF-8 text. It is included verbatim in the IPA. Anyone with the IPA can rename the file extension to .zip, extract the Payload directory, find your .env, and read every value. No reverse engineering required.
Some React Native and Expo projects end up shipping a .env.production because their build script copies it into the bundle resources by default. Some Capacitor and Cordova projects do the same because the web build step bundles the entire dist directory. The fix is to add the file to your build's exclusion list (react-native-config reads .env at build time and exposes constants, rather than bundling the file itself), and to confirm with unzip -l YourApp.ipa | grep env that no .env* file survives the archive step.
How do attackers actually pull secrets out of a shipped IPA?
The same way a curious reviewer would, if they cared to look. The toolchain is short:
- Extract the IPA. Either pull from the App Store with
ipatool, sideload to a jailbroken device and dump withfrida-ios-dump, or download from a paid IPA mirror. - Unzip. An IPA is a Zip file. Inside the Payload directory sits
YourApp.app, a bundle of resources, frameworks, and the Mach-O executable. - Run
strings YourApp.app/YourApp | grep -E "sk-|AKIA|AIza|xoxb". This single command surfaces OpenAI, AWS, Google, and Slack keys by their well-known prefixes, as researcher Spaceraccoon documented in his writeup on hunting low-hanging credentials in iOS apps. - Open
Info.plistwithplutil -pto find any custom keys you exposed for SDK configuration. - For deeper analysis, load the executable in Ghidra or Hopper and follow references from suspicious string literals to their call sites.
The investment is minutes, not hours. The reason teams treat hardcoded keys as a Storage class issue under OWASP MASWE-0005 is that the asymmetry between attacker effort and defender exposure is severe. Once a key is in the binary and the binary is in the App Store, the only fix is rotation at the provider.
What is the safer way to handle env variables on iOS?
Three patterns hold up under static analysis, in order of how much architectural rework they require.
The first is to keep the secret on your own backend and call your backend from the iOS client, passing only short-lived session tokens. The OpenAI key, the Stripe secret key, the database admin credential never reach the device. The user signs in, your backend mints a session, and the app holds only that session.
The second is to use Apple's Keychain for any credential the user owns (their OAuth refresh token, their session cookie, a per-device key generated on first launch). Keychain entries are encrypted with hardware-backed keys and bound to the device, so even a full file system dump on a jailbroken device cannot trivially read them, per Apple's Keychain Services documentation.
The third, when the first two are not feasible, is to obfuscate any bundled key using a generated Swift file that XOR encodes the value at build time and decodes at runtime, the pattern NSHipster sketches out. Obfuscation slows down casual extraction, but it does not stop a determined attacker with Frida hooked into the decode function. Treat it as a delay tactic, not a solution.
For builders shipping AI-generated iOS apps (Cursor, Replit Agent, Lovable, Windsurf), an external pre-submission scan helps catch the most common patterns before the app reaches the store. PTKD.com (https://ptkd.com) is one of the platforms that runs a static scan over compiled IPA and APK builds for credentials and OWASP MASVS storage issues, including the env variable patterns described above.
What to watch out for
A few patterns reliably end up shipping by accident.
.xcconfigfiles committed with real values. A teammate setsOPENAI_API_KEY = sk-real-keyinDebug.xcconfig, commits, and forgets that the build references it. The value compiles intoInfo.plistas a string entry. Audit your shippingInfo.plistwithplutil -pagainst your archived build.react-native-configmisused as a runtime secret store. The library reads.envat build time and exposes constants. Anything you put in.envends up in the bundle. Use it for build flags, not for credentials.- The "Apple will tell me if it is a problem" reflex. Review feedback does not name embedded secrets. Silence from the reviewer is not validation that your build is clean.
- Forgetting that TestFlight builds carry the same risk. TestFlight installs an IPA on the tester's device. A tester with technical skill can extract it and find the keys, just like an App Store user.
- Treating environment variable obfuscation as encryption. XOR with a hardcoded key is reversible by anyone who reads the decode function. It deters scrapers, not attackers with Frida.
Key takeaways
- App Review does not run a binary scan for environment variables or hardcoded credentials, and rejection on those grounds is rare. Treat that silence as the absence of evidence, not evidence of safety.
- Run scheme environment variables are debug-only and do not ship.
xcconfigvalues, generated Swift files,Info.plistentries, and bundled.envfiles all do, and they are readable from a downloaded IPA in minutes. - The fix is architectural: keep secrets on your backend, mint short-lived session tokens, store user credentials in Keychain. Obfuscation buys time but does not solve the problem.
- For an external pre-submission read on compiled IPA and APK builds, some teams run their builds through PTKD.com (https://ptkd.com) for an OWASP MASVS-aligned credential scan before uploading.


