iOS security: what Apple gives you and what you still have to build
Apple's security model provides application sandboxing, mandatory code signing, the Keychain for secure credential storage, the Secure Enclave for biometric data, and hardware-backed encryption for the file system. These are strong foundations.
They do not protect you from insecure code that misuses them. Storing passwords in UserDefaults instead of the Keychain breaks your security model instantly. Trusting server responses without proper certificate validation exposes your users to network interception. Shipping development credentials in your production binary hands hackers the keys to your backend.
Developers often rely too heavily on the iOS operating system to protect their application logic. They believe that because iPhones are notoriously hard to infect with malware, the apps themselves must be secure. Attackers do not attack the iPhone. They attack your specific app's business logic, your hardcoded secrets, and your backend APIs.
Apple builds a fortress. You build the vault inside it. If you leave the vault door wide open, the fortress walls do not matter. The iOS security architecture relies on strict hardware and software controls. The Secure Enclave Processor (SEP) handles cryptographic operations and biometric matching. The kernel enforces mandatory code signing. No executable runs on the device unless Apple or a trusted enterprise developer signs it. The application sandbox isolates your process. Your app cannot read another app's memory or files. The hardware encrypts the flash storage automatically.
These protections stop generic malware. They do not stop targeted attacks against your application logic. We perform iOS application penetration testing because developers misunderstand the threat model. You control the code. You control how data moves. If you misuse the APIs, the device secures nothing.
Attackers understand this asymmetry. They target the weakest link. They pull your binary from the device. They strip the FairPlay DRM encryption. They load the raw Mach-O binary into a disassembler. They read your unencrypted strings. They map your backend API architecture without sending a single network packet. They look for hardcoded AWS credentials, embedded Firebase tokens, and leftover staging URLs. They find them frequently.
Many developers assume the App Store review process acts as a security gate. It does not. Apple reviews your app for policy compliance. They check if you use private APIs. They check if you handle subscriptions correctly. They do not run a comprehensive iOS penetration testing engagement. They will not flag a broken authorization check. They will not stop you from saving a session token in a plaintext SQLite database. They will not prevent you from accepting self-signed certificates in your network stack.
The security of your fintech app rests entirely on your implementation. You must validate every input. You must encrypt sensitive data at rest using proper Keychain attributes. You must implement robust certificate pinning. You must treat the iOS device as a hostile environment. An attacker with a jailbroken iPhone owns the runtime. They control the operating system. They manipulate memory. They intercept function calls. You must build your app to withstand an environment where the operating system itself lies to your code. This is the reality of mobile security. This is exactly what we simulate during a manual iOS pentest.
Do not trust the App Store to find your security flaws. Book a manual iOS pentest today.
Book an iOS PentestSpecialized iOS Application Security Testing Tools and Services
A generic vulnerability scanner will not suffice for an iOS penetration test. Our iOS application penetration testing service utilizes a highly specialized suite of iOS application security testing tools. We do not rely on automated reports. To truly pentest iOS applications, we deploy Frida for dynamic instrumentation, Objection for runtime exploration, and Hopper or Ghidra for reverse engineering the compiled Mach-O binaries.
When clients request iOS app security testing or an iOS app pentesting engagement, they often worry about device-level data exposure. We perform rigorous iOS keychain security testing. We verify if your authentication tokens and cryptographic keys are stored with the correct kSecAttrAccessibleWhenUnlockedThisDeviceOnly flags. If your app stores sensitive session data in UserDefaults or CoreData instead of the Keychain, any attacker with physical access or a malicious backup can extract it.
The iOS penetration testing methodology
We do not run automated scanners on your source code. We perform aggressive manual testing against your compiled binary and your live backend infrastructure. We break the testing into four strict phases.
Our methodology mirrors the attack chain of a sophisticated threat actor. We assume zero knowledge of your source code. We assume full control over the physical device. We map the attack surface methodically. We find the business logic flaws that automated tools miss. We focus heavily on financial impact, data exfiltration, and privilege escalation.
Static analysis: examining the IPA
We start with the iOS app package (the IPA file). Static analysis does not require a device and does not require jailbreaking. It gives us a complete map of your application architecture.
# Extract and examine the IPA (it is a ZIP archive)
unzip YourApp.ipa -d extracted_app
# Check app entitlements
codesign -d --entitlements :- extracted_app/Payload/YourApp.app/YourApp
# Search for hardcoded secrets in the binary
strings extracted_app/Payload/YourApp.app/YourApp | \
grep -iE "(api.key|secret|password|token|sk-|pk_)" | head -30
# Review plist files for sensitive configuration
find extracted_app -name "*.plist" | xargs plutil -p
# Check Info.plist for insecure configurations
plutil -p extracted_app/Payload/YourApp.app/Info.plist | \
grep -E "(NSAllowsArbitraryLoads|NSAppTransportSecurity)" We hunt for hardcoded Firebase keys, AWS access tokens, and internal staging URLs. We check your entitlements file. We see if you left debugging flags enabled in your production build. We read every `.plist` file to find configuration mistakes. We read the raw Swift and Objective-C class names using the `class-dump` tool. This tells us exactly where to look when we begin runtime testing.
An IPA file is simply a compressed archive containing your application binary, resources, and metadata. We unpack it. We isolate the Mach-O executable. We inspect the load commands. We extract all plaintext strings embedded during compilation. Developers routinely leave test credentials, backend administrative URLs, and internal IP addresses in these strings. We harvest them automatically.
We scrutinize your app entitlements. Entitlements dictate what capabilities your app requests from the operating system. Overly permissive entitlements open massive security holes. If we see `com.apple.security.get-task-allow` set to true in a production build, we know the app permits attaching a debugger. This allows an attacker to manipulate execution flow trivially. We verify App Transport Security (ATS) exceptions. If you disable ATS globally with `NSAllowsArbitraryLoads`, you allow unencrypted HTTP traffic. This exposes user data to network interception instantly.
We extract class interfaces using tools like `dsdump` or `class-dump`. This exposes your entire internal architecture. We map your ViewControllers, your networking classes, and your cryptographic wrappers. We identify the exact methods responsible for biometric authentication, password hashing, and session management. This static map guides our dynamic testing phase. We identify third-party SDK dependencies. We check those libraries against known vulnerability databases. A secure application built on an insecure networking library remains insecure.
Traffic interception: bypassing SSL pinning
To see what your app sends to your server, we need to intercept its HTTPS traffic. If your app implements certificate pinning (it should), we bypass it. The most common iOS pinning implementations and our bypass approach for each:
- No pinning: Simple proxy setup with Burp Suite. We install our CA certificate on the device.
- ATS (App Transport Security) only: Disable ATS via iOS proxy settings. Not real pinning.
- TrustKit / custom URLSession pinning: SSL Kill Switch 2 patches the SecureTransport layer globally on jailbroken devices.
- Network extension pinning: Frida hooks to the specific validation delegate method.
- Flutter/React Native on iOS: Dart or JavaScript runtime-level hooks, framework-specific.
Once we bypass the pinning, we watch the traffic in Burp Suite. We see exactly what data the mobile app sends. We modify that data. We change payment amounts. We change destination account numbers. We test your server's ability to reject tampered data.
SSL pinning prevents Man-in-the-Middle (MitM) attacks. It forces the app to trust only your specific server certificate. Standard HTTP proxies fail against pinned connections. We must neutralize this control. On a jailbroken device, we manipulate the environment. We use SSL Kill Switch 2 to patch Apple's SecureTransport API directly in memory. This forces the device to accept any certificate we present. If you implement custom pinning at the application layer, we identify the exact validation routine. We write a custom Frida script. We hook the validation method and force it to return true regardless of the certificate presented.
Cross-platform frameworks require specialized bypass techniques. Flutter uses its own embedded BoringSSL library. It ignores the iOS system certificate store entirely. We locate the certificate verification function within the compiled Flutter engine. We patch the binary to bypass the check. React Native relies on the underlying iOS networking stack, but custom native modules complicate the process. We dissect the specific implementation and hook the relevant native bridge functions.
With the proxy established, the real iOS penetration testing begins. The mobile application becomes a hostile client. We capture every API request. We analyze the authorization headers. We test for Insecure Direct Object Reference (IDOR) vulnerabilities. We swap user IDs. We attempt to view accounts that belong to other users. We inject SQL payloads into search fields. We perform mass assignment attacks by adding unexpected parameters to JSON payloads. We test if your backend blindly trusts the amounts and currency types sent by the client. Client-side validation is irrelevant. We strip it away and attack the server directly.
Dynamic analysis: runtime testing on device
With traffic interception in place and Frida running on a jailbroken device, we test the running application. This includes modifying API responses to test how your app handles unexpected data, manipulating request parameters in transit, and hooking into Swift/Objective-C methods at runtime:
// Frida script: hook Swift class method at runtime
// Example: bypass a biometric check
ObjC.schedule(ObjC.mainQueue, function() {
// Hook the authentication result handler
var LAContext = ObjC.classes.LAContext;
var evaluatePolicy = LAContext['- evaluatePolicy:localizedReason:reply:'];
Interceptor.attach(evaluatePolicy.implementation, {
onEnter: function(args) {
// Replace the reply block to always return success
// This simulates an attacker bypassing biometric auth
// on a jailbroken device
}
});
}); We bypass your jailbreak detection logic. If your app checks for Cydia or specific jailbreak files, we patch those checks in memory to return false. If your app forces a logout when it detects a jailbroken state, we freeze the thread that triggers the logout. We own the runtime environment completely. We force your client-side security controls to fail.
Dynamic analysis exposes the behavior of your application in real time. We execute the app on a physical, jailbroken iPhone. We attach a debugger. We use Frida for dynamic binary instrumentation. This grants us absolute control over the execution flow. We inspect variables as they change. We trace function calls step by step. We map the exact sequence of events during a login process or a fund transfer.
Jailbreak detection provides a false sense of security. Developers implement checks for common jailbreak artifacts like the Cydia application, MobileSubstrate, or the SSH daemon. They check if the application can write to locations outside the sandbox. We find every single one of these checks. We hook the functions responsible for evaluating them. We overwrite their return values in memory. Your application believes it runs on a pristine, uncompromised device. We bypass debug prevention techniques like `ptrace` calls using the same methodology.
We target biometric authentication mechanisms. If your app relies on `LAContext` to evaluate a FaceID or TouchID policy, we intercept the result block. We force the block to execute the success path, regardless of whether the biometric scan matched or even occurred. We bypass passcode lock screens entirely. We manipulate application state variables. We toggle developer menus hidden in production builds. We force the application into unexpected states to test for crashes and unhandled exceptions.
We fuzz the Inter-Process Communication (IPC) mechanisms. We monitor the application log files in real time. Developers frequently log sensitive data like session tokens, plain text passwords, or Personally Identifiable Information (PII) to the iOS system log. Any app running on the device can potentially read these logs. We capture everything the app writes to `os_log` or `NSLog`. We identify information disclosure vulnerabilities that violate data privacy regulations instantly.
Data storage testing
We examine every location where your app stores data on the device and assess whether the storage mechanism matches the sensitivity of the data:
# On a jailbroken device: examine app data directory
# (Replace APP_BUNDLE_ID with your app's identifier)
ls -la /var/mobile/Containers/Data/Application/*/
# Keychain items (what should be here: auth tokens, passwords, keys)
# Tool: keychain-dumper
keychain-dumper
# UserDefaults (what should NOT be here: auth tokens, PII)
# Location: /var/mobile/Containers/Data/Application/UUID/Library/Preferences/
cat /var/mobile/Containers/Data/Application/UUID/Library/Preferences/com.yourapp.plist
# SQLite databases (Core Data, explicit SQLite)
find /var/mobile/Containers/Data/Application/UUID -name "*.sqlite" | \
xargs -I{} sqlite3 {} .tables UserDefaults is basically a plain text file. Many developers accidentally save the user's JSON Web Token (JWT) or their biometric refresh token in UserDefaults. We dump this file and read the tokens in plain text. We check your Core Data databases. We ensure you properly encrypt local SQLite files using SQLCipher. We verify you utilize the iOS Keychain correctly.
Data storage vulnerabilities represent the most common flaw in iOS applications. The iOS sandbox protects your data from other apps. It does not protect your data from physical access or device compromise. We navigate the filesystem manually. We locate the application's Documents, Library, and tmp directories. We analyze every file written during normal operation.
We scrutinize `NSUserDefaults`. It stores simple key-value pairs as an unencrypted XML property list. It offers zero cryptographic protection. We routinely extract authentication tokens, user PINs, and API keys from this file. We review your SQLite databases and Core Data stores. By default, iOS does not encrypt these databases individually. An attacker with physical access to an unlocked device, or remote access to a jailbroken device, can read the entire database schema and contents. We mandate the use of SQLCipher for sensitive local databases.
We attack your Keychain implementation. The Keychain provides secure, hardware-backed storage for cryptographic keys and credentials. However, its security depends entirely on the access control attributes you specify. If you use `kSecAttrAccessibleAlways`, the data remains accessible even when the device is locked. We use tools like `keychain-dumper` to extract every item your app stores. We verify you use the strictest possible attribute, typically `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`. We ensure sensitive data does not migrate via iTunes backups.
We check the `Cache.db` file. The `NSURLSession` framework caches HTTP requests and responses by default. This caching frequently captures sensitive API responses containing financial balances or personal data. We read this cache file directly. We verify you implement the correct cache-control headers on your backend and disable local caching for sensitive endpoints on the client. We review WebKit local storage and cookies if your app uses web views. We check for sensitive data remaining in memory. We take memory dumps and grep for plaintext passwords. We trigger the iOS background snapshot mechanism. When your app goes to the background, iOS takes a screenshot for the task switcher. We verify you obscure sensitive screens before the snapshot occurs.
Deep link and URL scheme testing
Deep links allow other apps and websites to navigate into your app. We test deep link handlers for input validation failures, OAuth state parameter bypass, and whether your app properly validates the source of a deep link before processing payment confirmation or authentication callbacks.
If your app registers a custom URI scheme like `myapp://`, any website can trigger it. We create a malicious website with a link to `myapp://transfer?amount=5000&to=hacker`. If a logged-in user clicks our link, your app might process the transfer automatically. We test every single URI scheme and Universal Link your application supports.
Custom URL schemes lack access control. Any app on the device can register the same scheme. If a malicious app registers `myapp://` before your app does, iOS might route your traffic to the attacker. This enables URL scheme hijacking. The malicious app intercepts sensitive data, like OAuth authorization codes or password reset tokens, intended for your application. We verify your transition to Universal Links. Universal Links rely on a verified `apple-app-site-association` (AASA) file hosted on your domain. This prevents hijacking because the operating system cryptographic verifies domain ownership.
We attack the handlers directly. When your app receives a deep link, a specific method processes the URL. We treat this URL as untrusted input. We fuzz the parameters. We inject malicious payloads into the query string. We test for path traversal, cross-site scripting (in web views), and SQL injection. We test the business logic. If a deep link confirms a transaction, we replay that link. We modify the parameters to see if the app executes the action without secondary user confirmation. This acts as a mobile equivalent to Cross-Site Request Forgery (CSRF).
We scrutinize your OAuth flows. Mobile apps often use custom URL schemes for OAuth callbacks. We test the implementation for missing PKCE (Proof Key for Code Exchange) requirements. We check if your app strictly validates the `state` parameter to prevent authorization code injection. We identify flaws where an attacker can link their account to a victim's session. We test every entry point thoroughly.
JWT stored in UserDefaults, extractable without Keychain authorization
During an iOS security assessment of a Nigerian lending app, we found that the user's JWT authentication token was stored in NSUserDefaults (UserDefaults in Swift). On a jailbroken device, reading UserDefaults requires no additional authentication: any process with access to the device's filesystem can read UserDefaults for any app. We extracted the token, used it to authenticate API requests on a completely different device, and accessed the user's full account including loan history and repayment schedule. Fix: move the JWT to the iOS Keychain with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly protection. Deployed in two hours.
Reverse engineering and code obfuscation checks
We do not stop at standard pentesting. We actively reverse engineer your compiled Swift logic using tools like Hopper Disassembler. We look at the raw Mach-O binary. We test how difficult it is for a competitor or a hacker to steal your proprietary algorithms.
We check if you strip symbols from your release builds. We check if you use code obfuscation. We check if your API endpoints reveal too much information about your backend architecture. We teach your team how to write code that actively resists reverse engineering attacks.
Reverse engineering exposes your intellectual property. It reveals your proprietary algorithms. It uncovers hidden API endpoints. We pull your binary and attack it statically. We load the Mach-O file into Ghidra, IDA Pro, or Hopper Disassembler. We bypass the Apple FairPlay DRM wrapper. We analyze the underlying assembly code. We reconstruct your application logic line by line.
Swift applications present unique challenges and opportunities for attackers. Swift retains significant metadata by default. We use demangling tools to convert obfuscated Swift symbols back into readable class and function names. This provides a blueprint of your architecture. We check your build settings. We verify that you strip symbols from your release builds. Leaving debugging symbols intact hands an attacker the source code structure on a silver platter.
We evaluate your code obfuscation techniques. Relying on default compilation is insufficient for high-value fintech applications. We test if you implement control flow flattening, string encryption, and symbol renaming. We attempt to bypass these protections. Obfuscation does not prevent reverse engineering entirely, but it significantly increases the cost and time required for an attacker to succeed. We determine if that cost is high enough to deter realistic threats.
We assess anti-tampering controls. We modify the binary. We resign it. We attempt to run the modified version on a standard device. We check if your application detects the modification and terminates execution. We verify the integrity checks. We ensure your backend APIs do not rely on client-side secrets that we just extracted from the binary. Client-side security requires defense-in-depth. We identify every missing layer of that defense. We provide actionable, technical remediation steps to harden your binary against direct manipulation.
Your iOS application is deployed in a hostile environment. You do not control the device. You do not control the operating system. You do not control the network. You only control your code and your backend infrastructure. A rigorous iOS penetration testing methodology exposes the flaws in your implementation before attackers exploit them. We find the vulnerabilities. You fix them. The application becomes secure.
Shipping an iOS app that handles money or sensitive user data? We will test it properly.
Book an iOS PentestFrequently asked questions
Do you need a jailbroken iPhone to run a proper iOS pentest?
For dynamic testing (runtime manipulation, Frida instrumentation, Keychain extraction), yes, a jailbroken device gives significantly deeper access. For static analysis (binary analysis, strings extraction, entitlement review), we can work with the IPA file directly without a jailbroken device. A complete iOS pentest uses both approaches.
Can you bypass iOS certificate pinning?
Yes. We use SSL Kill Switch 2 for jailbroken devices, which patches SecureTransport globally. For apps that implement pinning in custom frameworks or at the application layer, we use Frida to hook the specific validation function. We have bypassed certificate pinning on Swift, Objective-C, Flutter, and React Native iOS builds.
What data can be extracted from an iOS app?
On a jailbroken device: Keychain items (tokens, passwords, cryptographic keys), UserDefaults values, Core Data databases, SQLite databases, cached network responses, and application data files. Some of this is also accessible through iTunes backup extraction without jailbreaking, which is why backup exclusion flags matter.
Does iOS App Store review replace a penetration test?
No. Apple's App Store review checks for policy compliance, not security vulnerabilities. It will not find an insecure API call, a broken authorisation check, or data stored in UserDefaults that should be in the Keychain. App Store approval means your app passes Apple's distribution policy, not that it is secure.
Related reading
Blog: iOS Swift penetration testing: Keychain and pinning bypasses · Certificate pinning in mobile banking · iOS vs Android security for fintech
Blog: Secure session management in mobile banking · Deep link hijacking
Services: Mobile app penetration testing · Penetration testing · Reverse Engineering