When jailbreaking first became popular on iOS devices, iOS developers tried various methods to protect their applications from the uncertainty of piracy, etc. There are many ways to do this, including checking for the existence of Cydia, detecting whether the application can read files outside of its own sandbox, crashing the application when a debugger is detected, and so on.
However, it turns out that these defenses are not that effective. If the attacker has direct access to the physical device, these measures are no longer effective. For experts, they can effectively bypass these measures by making the device look like it is not jailbroken, which was possible in the past and is still possible. At the same time, for some jailbroken users, they may not want to do anything bad, but just want some cool features, such as a customizable home screen. With jailbreaking potentially becoming popular again recently, Apple has come up with its own solution. In iOS 14, the new App Attest API provides apps with a way to sign server requests in an attempt to prove to the server that these requests are coming from a legitimate version of the app. It is important to understand that App Attest does not tell the server "is this device jailbroken?" because this solution has been proven time and time again to be unworkable. Instead, its goal is to protect server requests to make it more difficult for attackers to create illegal versions of your app to unlock advanced features or embed cheats. Once again, since the attacker has physical access to the device, there is no way to fully protect your app in this case. Since the application cannot be trusted to protect itself, App Attest requires that the necessary work be done on the backend of the application to implement this security policy. Since this is Swift-related content, how the backend should handle it will not be introduced here, but it will be mentioned in passing. Generate a pair of keys to sign the request App Attest relies on the use of asymmetric public/private key pairs to work. The ultimate goal is for the application to sign the server request with the key and then send the data to the backend, where the public key is used to confirm the legitimacy of the request. If an attacker intercepts the request, he has no way to change the content, so it will not affect the backend verification. To generate a key pair, import the DeviceCheck framework and call the generateKey method of the DCAppAttestService singleton object:
The key pair generated by App Attest is securely stored in the device's Security Enclave. Since it cannot be accessed directly, this method returns a keyIdentifier property that can be used to find the corresponding key when needed. We need to store it so that we can use it to verify the application's request later. It is worth mentioning that not all types of devices support App Attest. If you check Apple's documentation, you will find that we need to check whether it is supported first and ask the server to downgrade to apply exceptions:
But don't do it! As mentioned before, an attacker can easily pretend that the device does not support this operation. Apple has no corresponding countermeasures. The reason for this check is more because some Macbooks do not have chips that support it. According to Guilherme Rambo's investigation, most iOS devices support this function, so for iOS applications, this compatibility check is not needed. Send the public key to the backend In order to sign the request, we need to provide the backend with a way to verify the signature. We need to provide the backend with access to the public key generated above to complete the verification. However, we cannot simply create a request to send the public key, because it is easy for an attacker to intercept the request and send their own public key, which would give them full control over what the application sends to the backend. The solution to this problem is to ask Apple to prove that the key we sent is from the legitimate version of the application. This can be done by calling the attestKey method, which receives the key's identifier as a parameter:
This method accesses the remote Apple server and returns an "attestation" object, which contains not only the public key, but also a lot of information about the application to indicate that this is a legitimate public key certified by Apple. After the client receives this object, it must send it in its entirety to the backend, which needs to perform multi-step verification to confirm that it has not been tampered with. If the "attestation" object is verified to be legitimate, the backend can safely extract the public key of the application from it. It is not clear whether Apple attempts to check if the user's device is jailbroken during this process. The documentation does not mention this, but they do note that App Attest cannot determine if the device is jailbroken, which at least suggests that they try. It is safe to say that there is no way to tell if the device is jailbroken, and the word attest only means that the request has not been intercepted or tampered with. The additional clientDataHash parameter of the attestation request has nothing to do with the verification process itself, but is critical to security. In fact, this request is very easy to replay, and an attacker can intercept the verification request and steal the "attestation" object sent from Apple, so that the same verification request can be "replayed" in an illegal version of the application to deceive the server. The solution to this problem is to simply not allow verification requests to be performed arbitrarily. The client can provide a one-time use token (or session ID) that the server expects to be used with the request to ensure its validity. If the same token is used twice, the request will fail. This is the purpose of clientDataHash: by providing a hashed version of the token with the verification request, Apple will embed it in the final object and provide a way for your server to extract it. With this, it is difficult for an attacker to create an illegal version of your application just by intercepting the request.
As mentioned before, Apple does not recommend that you reuse your passkey, but rather go through the entire process for each user account in your device. Because this request relies on remote Apple servers, it may fail. If the error is that the server is unavailable, Apple says you can retry, but if it is for other reasons, you should discard the key identifier and start the process again. For example, this may happen when a user reinstalls your app: the key you generated will still work in a normal app update, but the error will still occur after reinstalling the app, device migration, or restoring the device from backup. For these cases, your app needs to be able to re-execute the key generation process. From the server side, it's also worth mentioning that the "attestation" object also contains a receipt that your server can use to request fraud assessment indicators from Apple. This allows you to check the number of keys generated and the devices associated with them to detect possible fraud. Apple specifically mentions the possibility of an attack where a user could use one device to provide valid assertions to a jailbroken device, and this fraud assessment can be detected by targeting users with an abnormally high number of assertion requests. Encryption Request After verifying the validity of the key, the backend will have access to the public key. From now on, every time you handle sensitive content, you can securely sign the request. The generateAssertion method used for this purpose works very similar to the key verification, only this time you need to verify the request itself:
As before, the backend must support the use of one-time tokens to prevent replay attacks. This time, since the request itself is our clientDataHash, we add the token to the JSON. There is no limit to the number of assertions that can be made for a given key. However, despite this, they should generally be retained to protect sensitive information when your application makes requests, such as downloading content. In this case, the extra protection comes from the fact that the request is hashed and can only be used once. Since the entire request is signed by the private key, an attacker cannot simply intercept the request and use them to make their own request. They would have to figure out where the parameters of your request are coming from and manually try to sign them, which is more technical than simply attaching a proxy. As mentioned at the beginning, it is not impossible to crack this protection, it just takes more effort. Testing and implementation The App Attest service records a flag that you cannot reset. To prevent this, apps in non-production environments use a sandboxed version. If you want to test in a production environment, you should add the com.apple.developer.devicecheck.appattest-environment entitlement to your app and set its value to production. If you have a large user base, Apple recommends that you enable this feature gradually because requests to attestKey are limited by network speed. in conclusion By implementing this in both the client and the backend, it is much harder for an attacker to create an illegitimate version of your app. However, please note that this does not mean it is impossible! As mentioned before, you cannot be sure if a user has a jailbroken device, nor can you determine a way to prevent them from attacking your app. As with most security measures, the purpose of App Attest is to make this process difficult enough that only a very skilled and professional attacker would find a way into your app - and there are very few of them. |
<<: The 12 most concerned questions about iPhone 12
>>: Android is destroying open source, and it's not just Huawei that's affected
In recent years, due to the impact of the new crow...
Internet promotion, especially mobile Internet pr...
The second part of the top ten crisis public rela...
Bai Tan "Mang (Tik Tok Girl Version)" l...
When promoting products, app planners and promote...
In today's digital age, video has become an i...
The complete market plan is mainly divided into t...
There are many debates online about "raising...
In order to attract more users, many companies ar...
What is a crooked review? If it is not correct, i...
Admittedly, in many cases, the acquisition of new...
In fact, what is more worrying than the "inv...
How many editors dream of having 100,000+ fans, r...
There has been a lot of talk about "negative...
Recently, a couple in their 60s were diagnosed wi...