The token is legitimate, however I keep getting bad requests (400). The payload may not be accurate.
No document with the appropriate payload structure is visible to me.
Receipt.bin was tried, but the file content could not be verified.
Referring this URL: https://developer.apple.com/documentation/devicecheck/assessing-fraud-risk
Here is my server side Java code:
private static String sendAttestationWithPayload(String jwt, String keyId,
String attestationData, String clientData) throws Exception {
// Create JSON payload
JSONObject payload = new JSONObject();
payload.put("keyId", keyId);
payload.put("attestationData", attestationData);
payload.put("clientData", clientData);
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(APPLE_ATTESTATION_URL))
.header("Authorization", "Bearer " + jwt)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload.toString()))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
handleResponse(response);
return response.body();
}
DeviceCheck
RSS for tagAccess per-device, per-developer data that your associated server can use in its business logic using DeviceCheck.
Posts under DeviceCheck tag
16 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hi,
I run a service that protects API calls from Apple ecosystem apps with several layers of security, one of them being DeviceCheck's server-to-server functionality. All requests arrive with a DeviceCheck token that I send to Apple to validate.
Essentially I'm using the functionality listed here:
The server-to-server APIs also let you verify that the token you receive comes from your app on an Apple device.
https://developer.apple.com/documentation/devicecheck
However, occasionally I see huge bursts of traffic that contain valid DeviceCheck tokens from a scripter. I want to understand how they are generating them. It seems like they have identified a way to forge tokens.
Here are traffic patterns for my site. The scale of the y-axis is somewhat arbitrary due to how I'm sampling the requests, but you get the gist. You can see the dark green bars at the bottom are general traffic, and the light green is what we rejected (we have other layers besides DeviceCheck that reject traffic). Interestingly, though, all those light green requests contained valid device check tokens!
I have thousands of the tokens stored in a file for analysis.
Are there known ways that Apple knows of tokens being forged? I wanted to open a TSI for this but the flow requires an Xcode project, and there is no Xcode project to demonstrate this issue. I would really like to get in contact with someone from Apple that either works on DeviceCheck or supports it.
Hundreds of apps in the store depend on my service, and DeviceCheck forms a layer of security that I want to rely on. Obviously we can't solely rely on it, and we don't, but it does form an important layer of our defense. So I ask:
If you know of a way to forge tokens, please comment and I'll shoot you a DM
If you work at Apple and know who I can talk to, please help me work through the process to get in touch with them.
Thanks,
Lou
I heard and read in reddit and GitHub about that UIDevice.current.identifierForVendor.uuidString is restricted according to privacy. Its better use DeviceCheck API or create my own UUID. So is it correct? If its so please can you share apple privacy update about this?
We are trying to integrate "Device Check" and "Device Check - App attest" services to check device integrity and app integrity. We read apple documentation and could not locate the TTL(time-to live) for "Device Token" and "App Attestation Object". Could you let us what TTL for Device Token" and "App Attestation Object"? We can design our architecture based on your answer
We are having trouble with App Attest when built with different processors. We need to build an IPA to send to our testers. When the app is built using Intel processor, everything works. But when we built using a mac with processor M, them the App Attest process fails.
The error occurs in our backend while validating the attesation object. We are doing the validation as stated by this documentation: https://developer.apple.com/documentation/devicecheck/attestation-object-validation-guide
The process of validating the Attesation Object fails in the step 4, this one:
Obtain the value of the credCert extension with OID 1.2.840.113635.100.8.2, which is a DER-encoded ASN.1 sequence. Decode the sequence and extract the single octet string that it contains. Verify that the string equals nonce.
The problem is that the validation fails only when the app is built in a M processor machine.
In our server we do (using GO Lang) something like this:
if !bytes.Equal(nonce[:], unMarshalledCredCert.Bytes) {
// error
}
unMarshalledCredCert is the nonce extracted from the Attesation Object sent by the mobile application and nonce[:] is the nonce stored in our backend side cache.
What can this be?
Hi,
I'm getting 400 Missing or badly formatted authorization token everytime I call the following API from my local Server ( I tried calling this API from my app itself as well)
curl --location 'https://api.development.devicecheck.apple.com/v1/query_two_bits' \
--header 'Authorization: Bearer <<JWT-token>>' \
--header 'Content-Type: application/json' \
--data '{
"device_token": Token_fetched_from_Device_Check,
"transaction_id":"c6bdb659-0ee6-443d-88cb-a8f036dfc551",
"timestamp": 1721300244267
}'
"device_token" - I have generated from DeviceCheck framework
JWT-token - I generated using key from .p8 file generated from Apple developer portal, keyId of the same and the team Id ( I have individual account)
IMP Points-
I have created this .p8 file from apple developer account, and I did enable Device check option while creating the key.
I also tried calling this API after 7 hours ( and more then that as well) of creating key from the developer portal as I have read somewhere that the key gets activated after few hours.
I understand (again read somewhere) that the token created by DeviceCheck framework has some expiration time so I tried with freshly created token several times as well.
This is how I'm generating token using DeviceCheck -
if curDevice.isSupported{
DCDevice.current.generateToken { (data, error) in
if let data = data {
}
}
}
JWT token generation -
func createJWTToken(privateKey: String, keyID: String, teamID: String) -> String? {
// Set up the JWT header
var jwtHeader = Header()
jwtHeader.kid = keyID
// Set up the JWT claims
let jwtClaims = MyClaims(iss: teamID, iat: Date())
// Create the JWT
var jwt = JWT(header: jwtHeader, claims: jwtClaims)
// Convert the private key to Data
guard let privateKeyData = Data(base64Encoded: privateKey) else {
print("Invalid private key")
return nil
}
// Sign the JWT
let jwtSigner = JWTSigner.es256(privateKey: privateKeyData)
do {
let signedJWT = try jwt.sign(using: jwtSigner)
return signedJWT
} catch {
print("Failed to sign JWT: \(error)")
return nil
}
}
But no luck, please suggest something. any sort of help is much appreciated.
Thank you
I'm following the attestation object validation guide to check my attestation server validations, but having a different output of that it's expected in the documentation.
Everything goes well until the step 2, where it's created the SHA256 hash of the one-time challenge, then this hash it's appended to the end of the authenticator data from the decoded attestation object.
Here the generated client data hash is different from the one in the documentation, which also causes a different nonce value.
Full implementation at Go Playground: https://go.dev/play/p/DpL_H3L8yWV
// generate the SHA256 hash of the one-time challenge
challengeHash := sha256.Sum256([]byte(serverChallenge))
// append the one-time challenge hash to the end of the authenticator data
clientDataHash := append([]byte(att.AuthData), challengeHash[:]...)
// create a SHA256 hash of the composite item to create nonce
nonce := sha256.Sum256(clientDataHash)
Then I noticed that if the one-time challenge value it's just appended to the end of the authenticator data, the value it's correctly according to the documentation.
Full implementation at Go Playground: https://go.dev/play/p/qqN97SevJAB
// append the one-time challenge byte array to the end of the authenticator data
// this time not generating the SHA256 hash of the one-time challenge
clientDataHash := append([]byte(att.AuthData), []byte(serverChallenge)...)
// create a SHA256 hash of the composite item to create nonce
nonce := sha256.Sum256(clientDataHash)
My question is which of the implementations is correct, because if I didn't get it wrong it should be the first one and we would have an error in the documentation.
Whenever we call this API enpoint https://api.development.devicecheck.apple.com/v1/validate_device_token we received an 403. The issue started yesterday around 2:00 PM (Brasil - Brasília time).
Hi,
I'm having headaches with the debugging of my Unwanted Communication Reporting extension.
iPhone log says:
com.apple.IdentityLookup.MessageFilter[1774] <Error>: Extension's containing app (appID <private>) unauthorized to defer requests to host <private>
So I guess I have something wrong with my apple-app-site-association.
The AASA file I get with swcutil dl -d services.mydomain.com
{
classificationreport = {
apps = (
"<MYTEAMID>.com.mydomain.myapp",
"<MYTEAMID>.com.mydomain.myapp.unwanted"
);
};
}
where .com.mydomain.myapp is my containing app (bundle id).
and .com.mydomain.myapp.unwanted is my extension
The AASA file on the server is obviously in JSON format and correctly served from the server : services.mydomain.com
In the extension Info.plist I've set the following :
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>ILClassificationExtensionNetworkReportDestination</key>
<string>https://services.mydomain.com/path/unwanted/report</string>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.identitylookup.classification-ui</string>
</dict>
I suppose the reply from classificationResponse method is correct since MessageFilter "tries" to send the request.
Where am I mistakening ?
Difficult to debug... MessageFilter's log keeps interesting data as private (so there is no way to check what it's actually doing). And I've found no way to go deeper in the debug process.
Many thanks
Emmanuel
In our Enterprise account with Admin access, I can't see the option to create a DeviceCheck key needed for the App Attest functionality.
Is there a limitation preventing this key from being generated on Enterprise accounts?
Our service is using Apple device check api to prevent fraud happening.
https://developer.apple.com/documentation/devicecheck/accessing-and-modifying-per-device-data
But there is SSL connection timeout happen from June 1st.
/app $ curl --connect-timeout 5 -v https://api.devicecheck.apple.com
Trying 17.33.193.105:443...
Connected to api.devicecheck.apple.com (17.33.193.105) port 443 (#0)
ALPN, offering h2
ALPN, offering http/1.1
successfully set certificate verify locations:
CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
TLSv1.3 (OUT), TLS handshake, Client hello (1):
SSL connection timeout
Closing connection 0
curl: (28) SSL connection timeout
Does apply add access limitation for specific ips?
Hey,
the documentation for the generateToken() method states:
Your server uses the generated token in its requests to get or set the persistent bits for the current device. You should treat the token you receive in the completion block as single-use. Although the token remains valid long enough for your server to retry a specific request if necessary, you should not use a token multiple times. Instead, use this method to generate a new token.
Now, what I want to do in my backend is:
Read the current bits to decide if an action is allowed
If the action is allowed, update the bits
That's 2 calls to the DeviceCheck API, so I would need 2 tokens, right? But I want to do this in a single call to my backend.
Is it fine for the device to generate send 2 tokens at the same time? Or can I actually use a single token for both calls, and "single-use" just means I should not store it?
Thanks!
Hello,
Since the 21st of March 2024 around 6PM UTC I've been observing a very significant an increase (more than quadrupled) in P99 and P95 latency on https://data.appattest.apple.com/v1/attestationData
I'm calling other endpoints of the same API, and I'm not observing a similar increase there.
I tried submitting a report in Feedback Assistant but it's not working for me at the moment.
Hello,
I'm developing a server that uses the app attestation feature. During the development, I found the behavior that are not written in the document, I would like to inquire this.
When Apple server returns 404 for risk metric refresh request?
A month after the attestation, receipt is not past expiration time, but 404 is returned from Apple server when I try refresh. And this receipt succeeded in refreshing the risk metric normally if the attestation proceeds again.
This behavior is not in the document, but I wonder if it is intended.
Is there a case where an attestation has occurred but the risk metric value does not increase?
I found a case where attestation occurred twice on one device, but when both receipts were refreshed, the risk metric returned 1. Is this an expected behavior? If it is, I would like to know the detailed conditions under which it occurs.
Thank you.
response body is "service discovery failure"
It appeared by chance.. Does anyone know why?
A lot of our customers experienced failed App attest and always return error "DCErrorInvalidKey 3" invalidKey error on these iOS versions:
16.7.2 - iPhone 8
17.1.1, 17.1.2 - iPhone X, iPhone XS, iPhone XR, iPhone SE 2. iPhone 12, iPhone 12 Pro, iPhone 12 Pro Ma