iOS signing capabilities, links and protected data
Connect entitlements and application groups to actual callers, URL transitions and protected key use.
- 01Identify signed build
- 02Map capabilities
- 03Observe normal access
- 04Change caller or lock state
- 05Verify release result
- 06Clean fixtures
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Used in these playbooks#
Setup and invariant#
Prepare a Mac with Xcode tooling and an owned supported device. Record bundle ID, build, signing team, distribution channel, OS, and artifact hash. Keep device IPA, simulator build, enterprise build, and re-signed copy separate. Create two synthetic accounts and one private document whose marker can be recognized without collecting other data.
Invariant: links and extensions cannot borrow capabilities outside their intended trust relationship, and sensitive data/key use respects the promised authentication and lock state. Entitlements describe granted capabilities; they do not demonstrate that an unrelated app can exercise them. Apple enforces application-group relationships through signing and sandbox rules. App protection and groups.
Procedure and command sheet#
- Inspect the unchanged bundle. On macOS, inspect the main app and repeat for each embedded extension. The entitlement command writes to stdout; it does not re-sign the app.
plutil -p ./Payload/Lab.app/Info.plist
codesign -dv --verbose=4 ./Payload/Lab.app
codesign -d --entitlements - ./Payload/Lab.app
Capture CFBundleURLTypes, associated domains, App Groups, Keychain access groups, extension points, and ATS exceptions. A capability missing from Info.plist may be in the signature, so inspect both. OWASP entitlement extraction, OWASP plist tooling.
- Build a capability table. Use columns for main app, extension, signing identity, shared container, shared secret, intended consumer, and external input. In source-available work, locate storage and callback code:
rg -n 'SecItem(Add|CopyMatching)|kSecAttrAccessible|kSecAttrAccessGroup|LAContext|evaluatePolicy' ./source
rg -n 'openURL|continueUserActivity|WKScriptMessageHandler|containerURL' ./source
Matches identify implementation paths, not vulnerabilities. Trace the actual access group/class and the operation that uses the retrieved key; do not infer a security boundary from a biometric-looking screen alone.
Compare link transitions. Open one normal universal link and one custom-scheme route using an owned Notes/browser fixture. Repeat while logged out, after account switching, and with a mismatched synthetic resource. Record browser/app routing separately from account authorization. Universal links require website/app association; custom schemes have different ownership guarantees. For OAuth callbacks, reuse identity/session tests; for object access, use the authorization matrix. Apple universal-link model.
Test one protected-state transition. Establish legitimate access when unlocked, then lock the device and exercise only the supported background/extension path. Compare with a denied access class or an operation requiring fresh user presence. Record whether sensitive plaintext or usable key operations become available, not simply whether encrypted bytes remain on disk. Keychain accessibility, synchronization, and user-presence policy serve different purposes. Keychain data protection.
Confirm the caller model. Test extension input with a harmless imported document or URL through the normal share sheet. Reuse file boundaries and browser trust when files or WKWebView messages are involved. An attacker app signed with the target's team or a jailbreak granting container access invalidates an ordinary-app claim; disclose such constraints and seek an unchanged-build reproduction.
Completion and cleanup#
- Preserve exact build, signature, device state, input and expected denial.
- Explain why background access violates a product promise rather than an intended feature.
- Keep proof to the seeded document or one reversible operation.
- Remove synthetic links/documents and testing profiles; revoke lab sessions and restore the owned device configuration.
Native authentication continuation#
For browser-based sign-in, record the actual authentication session, callback scheme or associated-domain route, and the pending login or account-linking transaction. In the local identity fixture, begin independent A/B attempts and return A's callback while B is pending. Compare the resulting account and token decision with an untouched control. Route matching alone does not establish that a callback belongs to the current transaction. Reuse federation/token trust for the complete code/PKCE/issuer checks; RFC 8252's native-client model separates the external authorization agent from the native client. OAuth for native apps.
For passkeys or local authentication, record whether the protected operation is a remote session issuance or local use of a protected key. Trace the returned credential to the server and apply authenticator lifecycle. A successful Face ID prompt is not evidence that server-side challenge and account binding are correct. Inspect local paths without changing signing identities:
rg -n 'ASWebAuthenticationSession|ASAuthorization|code_verifier|redirect_uri|SecKeyCreateSignature' ./source
- Record callback routing, pending transaction and final account separately.
- Exercise cancellation/expiration and an account switch with synthetic identities.
- Preserve the valid control and confirm no useful credential is issued for the mismatched transaction.
Vulnerability reverse references#
- Mobile link handler trusts routing as authorization — An externally supplied custom or universal/app link reaches a protected client operation without validating the route input and current security context.
- Untrusted WebView content reaches a native bridge — A page or frame controlled outside the trusted app origin can invoke native capabilities through a shipped WebView bridge.
- Keychain accessibility violates the intended lock boundary — A sensitive item or protected key operation is available in a device state that the application’s security policy requires it to deny.
- Extension input crosses an iOS shared-container boundary — The main application trusts content written through a lower-trust extension/input path as if it were an authorized app action.
- Local authentication does not gate the protected operation — A local PIN or biometric screen is bypassed through another reachable path while the supposedly protected data/key operation remains usable.
- Private client content leaks through a lower-trust display or sharing channel — A private marker appears in a notification, background snapshot, clipboard or another user-visible channel outside its promised audience.
Nearby shared procedures#
Object, function, and property authorization · Authentication, recovery, federation, and session lifecycle · Trace input into queries, commands, and templates · Browser execution, cross-origin reads, and forged actions · Uploads, downloads, paths, archives, and XML · Workflow invariants, replay, idempotency, and races · REST, GraphQL, gRPC, WebSocket, and SSE permission parity · Source review: entry point to protected operation