When jailbreaking of iOS devices first became popular, it was common for iOS developers to try to defend their apps from users that altered their devices to enable piracy. There were many ways of doing that, which included checking for the existence of Cydia, checking if the app could read files outside its sandbox, crashing the app if it detects a debugger, and more.

As time has shown, these defensive measures were and still are a really bad idea. If an attacker has physical access to the device, there’s no way you can trust your app’s logic. It was and still is trivial for hackers to pretend their devices are not jailbroken and effectively bypass these measures. Besides, having a jailbroken device doesn’t mean the user wants to pirate content — some people just want cooler-looking home screens.

As (possibly) a response to jailbreaking becomes popular again in recent times, Apple has released their own measure to this problem. In iOS 14, the new App Attest APIs provide you a way to sign server requests as an attempt to prove to your server that they came from an uncompromised version of your app.

It’s important to know that App Attest is not a “is this device jailbroken?” check, as that has been proven over and over to be impossible to perform. Instead, it aims to protect server requests in order to make it harder for hackers to create compromised versions of your app that unlock premium features or inserts features like cheats. Note the word harder: as jailbreakers have physical access to their devices, nothing will completely safeguard you from fraud in this case.

As you can’t trust your app to protect itself, App Attest requires work on your backend to be fully implemented. I won’t go through the back end part of it as this is a Swift blog, but will at least mention how it works to show how it wraps things together.


Generating a Pair of Keys to Sign Requests

App Attest works through the use of an asymmetric public/secret pair of encryption keys. The intention, in the end, is for your app to sign server requests with the secret key, send the data to the backend and have it confirmed it to be true with the public one. If a hacker intercepts the request, it will not be able to alter its contents without making the backend’s subsequent validation fail.

To generate the keys, import the DeviceCheck framework and call the generateKey method from the DCAppAttestService singleton:

import DeviceCheck

let service = DCAppAttestService.shared
service.generateKey { (keyIdentifier, error) in
    guard error == nil else {
        return
    }
}

The keys generated by App Attest are safely stored in your device’s Security Enclave. As you can’t directly access it, the result of this method will be a keyIdentifier property that allows iOS to find the keys when needed. You need to store it so you can later validate your app’s requests.

It’s important to mention that App Attest is not supported by _all _types of devices, and if you look at Apple’s own documentation, they will ask you to first check if it’s supported and have your server support a fallback in case it’s not:

if service.isSupported { ... }

Do not do this! As said before, it’s trivial for a jailbreak user to “pretend” their device doesn’t support it. Apple doesn’t expand on this topic, but the reasons for this check to exist appears to be that there are some Macbooks that don’t have the necessary chip to support it. However, as investigated by Guilherme Rambo, it appears that every single iOS device supports it. For an iOS app, you do not need to do a compatibility check.

#xcode #programming #swift-programming #swift #ios

New in iOS 14: App Attest
18.00 GEEK