9.10 — Security Auditing & Pentest for iOS
Opening scenario
Before launch, the CTO asks: “Are we secure?” The honest answer requires structured assessment, not a gut feeling. This chapter is the workflow — what to scan with, what tools to install, what a pentester actually does to your IPA, and how to triage and disclose findings. By the end you can do an internal audit confidently before the external pentester arrives, so the report is smaller and the fixes are cheaper.
Context — the audit pyramid
┌──────────────┐
│ External │ $$$$
│ Pentest │
└──────────────┘
┌──────────────────────┐
│ Internal Pentest │ $$$
│ (red-team your app) │
└──────────────────────┘
┌────────────────────────────┐
│ Dynamic analysis (DAST) │ $$
│ Frida, mitmproxy, objection│
└────────────────────────────┘
┌──────────────────────────────────┐
│ Static analysis (SAST) │ $
│ SwiftLint security, semgrep, MobSF│
└──────────────────────────────────┘
┌────────────────────────────────────────────┐
│ Build-time checks + code review │ ¢
│ Every PR; baseline │
└────────────────────────────────────────────┘
The bottom layers are cheap and continuous; the top layers are expensive and periodic. Shift findings down as much as possible — the fewer surprises in the external pentest, the better.
Static analysis tools
SwiftLint security rules
Already covered in 9.7 — enable force_unwrapping, force_cast, and custom rules for UserDefaults-secrets and NSPredicate format interpolation. Run in CI on every PR.
semgrep
brew install semgrep
semgrep --config "p/swift" --config "p/security-audit" .
The semgrep p/swift ruleset includes ~50 iOS-specific patterns (hardcoded API keys, weak crypto, unsafe network configs). False positive rate is moderate; baseline first, then enforce in CI.
MobSF (Mobile Security Framework)
Open-source IPA static + dynamic analyzer. Upload your .ipa or .zip of source, get a report covering:
- Hardcoded secrets (entropy + regex scan)
- Insecure permissions in entitlements
- ATS misconfigurations
- Insecure URLs and schemes
- Suspicious third-party libraries
- Class-dump output of ObjC interfaces
git clone https://github.com/MobSF/Mobile-Security-Framework-MobSF.git
cd Mobile-Security-Framework-MobSF
./setup.sh
./run.sh 127.0.0.1:8000
# upload IPA via web UI
MobSF is the iOS equivalent of “running OWASP ZAP on a web app.” Run before every release.
Trivy / Snyk for SPM dependencies
trivy fs --scanners vuln,secret,misconfig .
Scans Package.resolved against the GHSA database for known CVEs in your transitive dependencies. Integrate into CI; fail builds on high-severity findings.
Dynamic analysis tools
mitmproxy
brew install mitmproxy
mitmproxy --listen-port 8080
# On simulator: System Settings → Network → Wi-Fi → Configure Proxy → 127.0.0.1:8080
# On device: install mitmproxy CA via http://mitm.it after configuring proxy
Reveals every request your app makes. Use to:
- Verify pinning blocks intercept (pinning should refuse the mitmproxy CA)
- Audit headers — are auth tokens correctly attached only to authorized hosts?
- Spot unintended endpoints (analytics SDK calling its own server with PII)
- Replay requests to test server-side authorization
Frida + objection
pip install frida-tools objection
objection --gadget "com.acme.app" explore
In the objection REPL on a jailbroken device or repackaged IPA:
ios keychain dump
ios sslpinning disable
ios cookies get
ios nsuserdefaults get
memory list modules
ios hooking list classes
ios hooking watch class_method "AuthManager.login"
This is what a pentester does on day one. Run these yourself first; if pinning is bypassable in 30 seconds, fix it before the report lands.
IDA Pro / Hopper / Ghidra
Reverse-engineering disassemblers. Walk the binary’s auth path, look for hardcoded strings, identify the cryptographic primitives in use. Skip unless you have time to learn the tool; rely on MobSF + class-dump for the 80 % case.
A typical internal pentest workflow
- Recon — install IPA on jailbroken test device. Run
stringsandclass-dump. Inspect Info.plist and entitlements. Check for embedded secrets. - Storage audit — dump Keychain, UserDefaults, Documents, Library. Look for tokens, PII, encryption keys.
- Network audit — proxy through mitmproxy with the device’s CA trust subverted. Check pinning, headers, request payloads, plaintext fields.
- Runtime audit — Frida-hook critical methods (
AuthManager.login,Keychain.get,URLSession.dataTask). Verify business logic enforcement isn’t bypassable. - Server audit — replay captured requests with modified parameters. Test for IDOR, missing authz, parameter tampering.
- Privacy audit — match nutrition labels to actual network behavior. Check Privacy Manifest accuracy.
- Documentation — every finding gets severity (Critical/High/Medium/Low/Informational), OWASP M-category, repro steps, fix recommendation, and code reference.
A focused internal audit takes 1-3 days for a mid-sized app. Repeat quarterly + before major releases.
Severity rubric
| Severity | Criteria | Example |
|---|---|---|
| Critical | Remote exploitable, immediate user/account compromise | Hardcoded admin API key |
| High | Account takeover with attacker proximity or interaction | TLS not pinned + token in plaintext |
| Medium | Information disclosure, weak crypto, missing defense-in-depth | UserDefaults storing email |
| Low | Hardening recommendation, no direct impact | Missing jailbreak detection |
| Informational | Best practice violation | Forgotten debug log |
Don’t inflate severity — bigwig reports lose credibility. Don’t deflate either — Critical findings need executive visibility.
Responsible disclosure
When you find a vulnerability in someone else’s iOS app or SDK:
- Document with proof-of-concept, environment, version, expected vs actual behavior.
- Contact via the vendor’s security@ email or security.txt; allow at least 90 days for remediation before public disclosure.
- Coordinate the disclosure timeline; vendors appreciate this, attackers don’t get this courtesy.
- Apple Security Bounty — vulnerabilities in iOS, macOS, watchOS, or Apple platforms qualify for Apple’s bounty program. Reports go via security@apple.com with PGP-encrypted payloads. Bounties range $5k to $1M+ depending on severity and demo quality.
When someone reports a vulnerability in your app:
- Acknowledge within 24 hours.
- Triage with severity assessment within 72 hours.
- Communicate timeline back to reporter.
- Patch with regression test.
- Disclose publicly after fix is in users’ hands (App Store rollout completion).
- Credit the reporter if they want it.
A public security.txt (RFC 9116) at https://acme.com/.well-known/security.txt listing contact and policy is table stakes.
Pre-release security checklist
□ STRIP_SWIFT_SYMBOLS = YES in release config
□ No NSAllowsArbitraryLoads in release Info.plist
□ All ATS exceptions documented and justified
□ PrivacyInfo.xcprivacy present and accurate
□ All required usage description strings (Info.plist)
□ Privacy nutrition labels updated for current release
□ TLS pinning active with primary + backup pins
□ Pinning kill-switch verified working
□ Keychain accessibility audited (no Always, no plain Whenever)
□ No tokens or PII in UserDefaults (lint-enforced)
□ Biometric-gated keys for high-value operations
□ App Attest enabled on value-bearing endpoints
□ Jailbreak detection signals reporting to server
□ SwiftLint security rules passing
□ semgrep security ruleset passing
□ MobSF report reviewed; all High+ findings resolved
□ Trivy/Snyk passing on Package.resolved
□ Account deletion flow tested in-app
□ Crash reporter PII filters verified
□ Internal pentest within last quarter
□ Security review sign-off from designated owner
Pin this in the repo. Required check before pressing the App Store submit button.
In the wild
The Signal team publishes regular security reviews — third-party audits by NCC Group and others, reports made public. Lyft, Robinhood, Coinbase, and most fintechs use a combination of in-house security teams plus annual external pentests by firms like NCC Group, Trail of Bits, or Cure53. The pattern across mature security programs: continuous SAST in CI, quarterly internal pentests, annual external pentests, plus a public bug bounty for ongoing crowdsourced testing.
Common misconceptions
- “External pentest = secure.” A pentest is a snapshot, not a guarantee. Continuous internal review + a strong baseline is what produces durable security.
- “Tools find everything.” Static + dynamic analysis catch the common cases. Architectural flaws (broken auth flow, missing authz) require human review.
- “We don’t need a bug bounty until we’re big.” Even a coordinated-disclosure email is enough. The cost is responsiveness, the benefit is finding issues before attackers do.
- “Annual pentest is fine.” Annual pentest + zero internal review means findings get rediscovered every year. Build internal capability.
- “Apple’s review checks security.” App Store Review catches policy and a small subset of security issues (ATS misconfigs, missing privacy strings). It does not audit your auth architecture or key management.
Seasoned engineer’s take
Security is a process, not a product. The most secure iOS apps aren’t the ones with the most defensive code — they’re the ones with the most disciplined review cycles. Bake static analysis into every PR so the baseline never regresses. Run an internal pentest every quarter so external pentests find architectural improvements, not blocking critical bugs. Maintain a security.txt and respond fast to reporters. Build the muscle of writing post-fix retrospectives so each finding teaches the team and reduces the next finding’s likelihood.
TIP: After every external pentest, hold a retrospective. Categorize each finding: what static check would have caught this? What internal pentest checklist item missed it? Update the checklist. Compound learning beats stronger one-time defenses.
WARNING: Don’t ship security fixes silently. Public release notes for security-affecting versions (“This release contains security improvements; please update”) help users prioritize. Quiet patches make users complacent about updates and erode the next time you need them to act fast.
Interview corner
Junior: “How would you audit an iOS app for security issues?” Run static analysis (SwiftLint security rules, semgrep, MobSF), then dynamic analysis through mitmproxy and Frida on a jailbroken test device. Check the Info.plist, Privacy Manifest, and entitlements. Map findings to OWASP Mobile Top 10.
Mid: “What would you do with a Critical finding two days before launch?” First, validate: reproduce the finding, confirm severity, assess blast radius. Second, communicate: alert the security owner and product leadership with timeline impact. Third, decide: fix-and-ship (if fix is contained and testable), kill-switch (if remote-configurable), or delay (if neither). Document the decision and reasoning. After ship, retrospective with a focus on how the finding survived to two days before launch — what review caught it, what review missed it, what we change next time.
Senior: “Design the security audit program for a Series B fintech with 40 engineers and one security person.” Three concentric loops. Innermost: every PR triggers SwiftLint + semgrep + Trivy in CI; broken security rules block merge. Security person reviews PRs touching auth, payments, key management — flagged by codeowners on those directories. Middle loop: monthly internal pentest by the security person on one new release per month, rotating focus areas (auth one month, networking next, storage next). Findings go into a tracked backlog with SLAs by severity. Outermost: annual external pentest by NCC Group or Trail of Bits, scoped to highest-value flows; pre-pentest, run an internal mock-pentest to surface and fix easy findings so external time is spent on architecture. Public security.txt, coordinated-disclosure policy, response SLAs published. After year one, add a managed bug bounty via HackerOne or Bugcrowd. The 40-engineer team’s part: secure-coding training onboarding, security-checklist signoff on every release, blameless retrospectives on findings. The system scales with engineering headcount without scaling the security team linearly.
Red-flag answer: “We’ll get a pentest before launch.” Reveals point-in-time thinking instead of process thinking.
Lab preview
Lab 9.3 (Security Audit) walks you through a complete internal pentest workflow: static analysis with MobSF, dynamic analysis with mitmproxy and objection, finding 8 deliberate vulnerabilities, categorizing by OWASP M-number, and producing a remediation plan with prioritization.
This concludes the Phase 9 chapters. Continue to the labs to put it into practice:
Then continue to Phase 10 — Deployment & CI/CD.