The Always location authorization on iOS is one of the most powerful entitlements a third-party app can request, and one of the least carefully threat-modeled. Most teams treat it as a UX problem, the user sees the “Allow While Using App” / “Allow Once” / “Don’t Allow” prompt; if they pick “Allow” then “Change to Always” later in Settings, the app gets background updates. Job done.
What that frames as a permission becomes, from a security perspective, four distinct surfaces:
- Persistent fingerprinting, coarse-or-precise location, every N seconds, even when the app is not running
- Lateral data movement, your app’s location updates can be observed indirectly by any other app sharing the user’s Apple ID via iCloud or by sibling apps from your own organization
- Battery-level oracle, the act of requesting background location modifies your app’s process scheduling in ways an attacker can detect from another app
- Hardware-attestation degradation,
CLLocationManageraccess subtly changes the Secure Enclave’s policy posture for some adjacent APIs
For an app handling regulated data, health, financial, location-aware authentication, the threat model around background location deserves more than a one-line “we use location for X.” Here’s the full picture as of iOS 19.
Surface 1: Persistent fingerprinting
When your app requests Always authorization and the user grants it, your app receives didUpdateLocations callbacks even when the app is in suspended state. iOS wakes your app briefly to deliver the callback, runs your locationManager(_:didUpdateLocations:) for up to 10 seconds, then re-suspends.
This is well-documented. What’s less-documented is what an attacker who’s compromised the device can do with this:
- Inference of a user’s home + workplace by averaging GPS points over a 30-day window. Even with coarse (city-level) data, the option iOS exposes via
CLLocationAccuracy.kCLLocationAccuracyKilometer, the home/work pair is identifiable with ~95% precision after 7 days. - Identification of social-graph members by clustering locations against the times-of-day they appear. Two devices that consistently appear at the same coordinates at the same times share an inferred relationship.
- Routine deviation alerting, if the device’s location pattern breaks from baseline (the user travels somewhere unusual), that anomaly itself is exfiltrable signal.
An attacker doesn’t need to compromise your backend’s storage to extract this. They need access to your app’s local CoreData / Realm / SQLite cache that’s been storing location history “for offline use.” That cache is a high-value forensic target.
Recommendation: don’t persist location history beyond the immediate operational window. If you need 30 days of history, store hashes-of-coarse-grid-cells, not lat/lon. The hashes preserve “did the device visit cell X” without preserving “exact coordinates at time T.”
If your organization ships two apps that both use background location and both sync to the same iCloud container or share a keychain access group, location data can leak between them. The user might have granted Always to app A but only While-Using to app B; if A writes location to a shared keychain and B reads from the same keychain, B has Always-equivalent data with the user’s WhileUsing-only consent.
This is structurally allowed (it’s your apps, your keychain group, your iCloud container) but it’s the kind of finding that surfaces in app-privacy audits and surprises an integrator. The fix is structural: location data writes go to per-app keychain entries (kSecAttrAccessGroup set to the app’s bundle, not the shared group), and inter-app communication explicitly omits location attributes.
xcprivacy-lint flags shared-keychain writes that include keys whose name pattern suggests location data (*Location*, *Coords*, *GeoFence*).
Surface 3: Battery-level oracle
When your app holds an active CLLocationManager, iOS adjusts your process’s GPS-radio usage. The radio’s duty cycle is observable through UIDevice.batteryLevel deltas, a sibling app on the device can poll battery level over 5-minute intervals and detect when your app is actively GPS-active vs idle.
Most apps don’t expose this as a side channel because they don’t ALSO ship a sibling app on the device. For a multi-app organization (e.g., a bank’s main app + their wealth-management spinoff), this is real. The bank’s main app holds Always location for transaction-fraud detection; the wealth-management app holds no location auth at all; but the WM app can infer when the main app is doing GPS-intensive work and correlate that to user behavior the user did not consent to share.
Mitigation: use the CLActivityType.otherNavigation activity type instead of automotive, which reduces GPS duty cycle when stationary and minimizes the battery-delta signal.
Surface 4: Hardware-attestation interaction
The Secure Enclave’s biometryCurrentSet policy, used by EnclaveVault, InputGuard, and BehaviorGuard for invalidating keys on Face ID enrollment changes, operates under iOS’s biometric subsystem. When CLLocationManager is active in background, iOS keeps the device’s “active app” telemetry busier; this nudges some adjacent biometric posture flags.
The practical effect: a biometric prompt that would have succeeded on first try might require a second attempt because iOS’s biometric subsystem is slightly more cautious about “this looks like an unusual context.”
This isn’t a security weakness, it’s a UX side effect. But it’s the kind of thing that surfaces in user-reported issues without a clear root cause if you don’t know it can happen.
What RuntimeGuard SDK does with location
RuntimeGuard SDK doesn’t request location authorization itself. The host that already holds location authorization can compare the device’s current coarse location against the device’s stated locale + timezone and fold that mismatch into its own risk decision alongside RuntimeGuard’s SecurityReport. Where RuntimeGuard’s own environment checks sit is laid out in the RuntimeGuard threat model.
// Optional usage: arm RuntimeGuard, then read the SecurityReport.
let runtimeGuard: any RuntimeGuard = RuntimeGuardCore()
try await runtimeGuard.start(apiKey: "sub_...", environment: .production)
// If the host already requests Always location for its own reasons, it can
// feed the observed locale/timezone/coordinate mismatch into its own risk
// scoring alongside RuntimeGuard's report; RuntimeGuard never requests
// location itself.
let report = try await runtimeGuard.assess()
The use case is fraud detection: a device with timezone=“America/New_York”, system locale=“en-US”, but coarse GPS = “Lisbon” is in a suspicious state. The user might be traveling, legitimate, but the combination is a useful signal for risk scoring. The host reads only the country-level inference and the locale/timezone fields from the standard iOS APIs; the precise location never needs to leave the host.
If the host app doesn’t have location authorization, this comparison simply isn’t available, and RuntimeGuard SDK does NOT prompt for location independently. The choice to request Always is the host’s; RuntimeGuard SDK rides along on the user’s existing consent rather than requesting any of its own.
The threat model in five lines
For an app shipping background location:
- Treat persisted location history as PII at rest. Encrypt; minimize retention; hash to grid cells where possible.
- Don’t share location across apps via shared keychain / iCloud container. Per-app keychain entries only.
- Use CLActivityType.otherNavigation rather than
automotiveto reduce duty cycle. - Document the location use clearly in PrivacyInfo.xcprivacy + App Privacy nutrition label. Apple’s review process catches inconsistent declarations.
- Surface the location-vs-locale signal to your fraud / risk engine. It’s a free signal for any app already holding the location authorization.
Background location on iOS is one of the few entitlements where the security threat model substantively exceeds the UX threat model. Most teams ship it on UX-only reasoning. The structural fix is to threat-model it as the persistent surface it actually is, and to use the side-channel signals it gives off as defensive observations, not just user features.
Engineering opinion, not advice. This post reflects the author's engineering reasoning at time of publication. It is not professional security, legal, financial, or compliance advice; do not rely on it as a substitute for review by qualified professionals for your specific situation. Posts may become outdated as iOS, Apple frameworks, attacker techniques, and our own SDKs evolve. The canonical source of truth for shipped SDK behavior is /docs and /changelog.

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.