Tüm Yazılar
KategoriSecurity
Okuma Süresi
15 dk
Yayın Tarihi
2025-11-06
Kelime Sayısı
3.239kelime

Kahveni hazırla - bu içerikli bir makale!

App Attest + Play Integrity: Sunucu Tarafı Doğrulama

Özet

App Attest ve Play Integrity token'larını sunucuda nasıl doğrulayacağını, challenge/nonce ile replay korumasını ve iki platformu tek backend'de birleştirmeyi resmi dokümanlara dayanarak anlatıyorum.

  • App Attest sunucuda sertifika zinciri, nonce türetimi, RP ID hash'i, counter=0 ve aaguid kontrolüyle doğrulanır.
  • Play Integrity'de deviceIntegrity, appRecognitionVerdict ve appLicensingVerdict ayrı ayrı değerlendirilmelidir.
  • Replay koruması, sunucunun ürettiği kısa ömürlü challenge/nonce'ın attestation veya requestHash'e gömülmesine dayanır.
  • Attestation'ı önce sessiz logla modunda açıp başarısızlık dağılımını gözlemlemek, meşru kullanıcıları yanlışlıkla engellemeyi önler.
App Attest + Play Integrity: Sunucu Tarafı Doğrulama

iOS ve Android istemcilerinden gelen her isteğe güvenip güvenemeyeceğini soran her backend ekibi er ya da geç aynı duvara çarpar: cihazda çalışan kod, saldırganın elinde değiştirilebilir. App Attest ve Play Integrity, bu soruyu istemci tarafında değil sunucu tarafında yanıtlamanı sağlayan iki resmi mekanizma — ama ikisi de doğru kurulmazsa yalnızca yanlış bir güven duygusu üretir. Bu yazıda App Attest ve Play Integrity token'larını sunucuda nasıl doğrulayacağını, replay saldırılarına karşı challenge/nonce mekaniğini ve iki platformu tek backend'de nasıl birleştireceğini Apple ve Android'in resmi dokümanlarına birebir dayanarak anlatıyorum.

💡 Pro Tip: App Attest ve Play Integrity yalnızca "bu istek gerçek bir cihazdan mı geldi" sorusuna cevap verir — kullanıcı kimliğini doğrulamaz. İkisini her zaman mevcut authentication katmanının üstüne, ayrı bir sinyal olarak ekle; yerine koyma.

İçindekiler

Tehdit Modeli: Sahte İstemci, Emülatör ve Replay Saldırıları

Apple'ın kendi ifadesiyle, bir backend istemci kodunun kendi kendini doğrulamasına güvenemez: "You can't rely on your app's logic to perform security checks on itself because a compromised app can falsify the results." Bu cümle, App Attest'in var olma nedenini özetliyor. Saldırganın elinde üç temel araç var: (1) uygulamanın API isteklerini taklit eden özel bir istemci yazmak, (2) emülatör veya rootlanmış/jailbreak'li bir cihazda gerçek uygulamayı çalıştırıp trafiği araya girerek değiştirmek, (3) daha önce yakalanmış geçerli bir isteği veya token'ı tekrar göndermek (replay). Attestation servisleri bu üç senaryoya karşı farklı savunma katmanları sunar: donanım destekli anahtar imzası sahte istemciyi eler, cihaz bütünlüğü sinyalleri emülatörü ve değiştirilmiş ortamı işaretler, tek kullanımlık challenge/nonce ise replay saldırısını ciddi biçimde zorlaştırır. Bu üçü birlikte çalışmazsa sistemin tamamı zayıflar — örneğin nonce doğru üretilse bile sunucu onu kontrol etmiyorsa hiçbir koruma sağlamaz.

App Attest Akışı: Anahtar Üretimi, Attestation ve Assertion

App Attest üç aşamalı bir protokol: anahtar üretimi, attestation (bir kez) ve assertion (her istekte). Önce cihaz DCAppAttestService ile donanım destekli bir açık/kapalı anahtar çifti üretir ve sana bir keyId döner. Ardından bu anahtarı Apple'a attest ettirirsin — bu adım, sunucudan aldığın tek-kullanımlık bir challenge'ın hash'ini isteğe gömer: "attestation embeds the hash of a unique, one-time challenge from your server." Apple, challenge'ın en az 16 byte olmasını, yeterli entropiye sahip olması için önerir: "The challenge should be at least 16 bytes in length to ensure sufficient entropy to ensure guessing them is infeasible." Attestation başarılı olursa Apple sana bir sertifika zinciri (x5c) ve receipt döner; zinciri doğrular, içinden çıkan public key'i ve receipt'i saklarsın.

swift
1import DeviceCheck
2import CryptoKit
3 
4func attestNewKey(serverChallenge: Data) async throws -> (keyId: String, attestation: Data) {
5 let service = DCAppAttestService.shared
6 guard service.isSupported else {
7 throw AttestError.notSupported
8 }
9 let keyId = try await service.generateKey()
10 let clientDataHash = Data(SHA256.hash(data: serverChallenge))
11 let attestation = try await service.attestKey(keyId, clientDataHash: clientDataHash)
12 return (keyId, attestation)
13}

Attestation'dan assertion'a geçiş

Attestation'dan sonraki her API çağrısında ise "assertion" üretirsin — bu da replay'i önlemek için yine sunucudan gelen bir challenge kullanır: "You use a challenge here, like for attestation, to avoid replay attacks." Apple, üretilen anahtarların normal uygulama güncellemelerinde geçerli kaldığını ama yeniden kurulumda, cihaz göçünde veya restore işleminde geçersiz olduğunu belirtiyor — bu yüzden backend'in "anahtar artık geçersiz" durumunu zarifçe (kullanıcıyı yeniden onboarding'e sokmadan) ele alması gerekir.

Sunucu Tarafında App Attest Doğrulama Adımları

Sunucudaki doğrulama, Apple'ın "Validating apps that connect to your server" dokümanında sırayla tanımlanan bir adımlar zinciridir. Önce attestation objesindeki x5c sertifika zincirini Apple'ın kök sertifikasına kadar doğrularsın. Ardından authenticator data'nın sonuna clientDataHash'i ekleyip nonce'u yeniden türetirsin: "Generate a new SHA256 hash of the composite item to create nonce." Bu nonce'un, sertifikanın OID 1.2.840.113635.100.8.2 uzantısındaki değerle birebir eşleşmesi gerekir. Sonra public key ile keyId'nin eşleştiğini, App ID'nin SHA256 hash'inin authenticator data'nın RP ID hash'iyle aynı olduğunu doğrularsın: "Compute the SHA256 hash of your app's App ID, and verify that it's the same as the authenticator data's RP ID hash." Ardından counter'ın sıfır olduğunu ve aaguid'in ortamla uyumlu olduğunu doğrularsın. Liste burada da bitmiyor: authenticator data'daki credentialId alanının anahtar tanımlayıcısıyla aynı olduğunu, extensions CBOR sözlüğündeki apple_validation_category_01 ve apple_bundle_version_01 değerlerini de kontrol etmen gerekir.

typescript
1// Node.js — App Attest doğrulama iskeleti (basitleştirilmiş)
2import { createHash } from "node:crypto";
3 
4function verifyAppAttestCounterAndEnv(
5 authData: AuthenticatorData,
6 env: "production" | "development",
7) {
8 // 1) counter alanı 0 olmalı
9 if (authData.counter !== 0) {
10 throw new Error("Replay şüphesi: counter 0 değil");
11 }
12 // 2) aaguid ortamla uyumlu olmalı
13 const expected =
14 env === "development"
15 ? "appattestdevelop"
16 : "appattest\u0000\u0000\u0000\u0000\u0000\u0000\u0000";
17 if (authData.aaguid !== expected) {
18 throw new Error("aaguid beklenen değerle eşleşmiyor");
19 }
20}
21 
22function deriveNonce(authData: Buffer, clientDataHash: Buffer): Buffer {
23 return createHash("sha256")
24 .update(Buffer.concat([authData, clientDataHash]))
25 .digest();
26}

Apple, attestation başarılı olduğunda public key'i ve receipt'i hemen kaydetmeni öneriyor: "When attestation succeeds, independently verify and store the receipt immediately." Ayrıca replay'e karşı ek bir önlem olarak aynı public key'in başka bir kullanıcı hesabıyla daha önce ilişkilendirilmemiş olduğunu kontrol etmeni tavsiye ediyor: "As an added protection against replay attacks, make sure that the public key doesn't already have an association with another user." Bu kontrolü atlarsan, bir saldırgan çalınmış bir anahtarı farklı hesaplarda yeniden kullanabilir.

Doğrulanan alanların özeti

Sandbox ortamında çalışırken aaguid alanının farklı bir değer taşıdığını da bilmen gerekiyor — Apple sandbox attestation'larında "appattestsandbox" değerini bekliyor. Aşağıdaki tablo, App Attest'in sunucu tarafında kontrol ettiği alanları ve beklenen değerleri özetliyor.

Kontrol Alanı
Beklenen Değer
Amaç
Sertifika zinciri (x5c)
Apple kök sertifikasına kadar geçerli
İstemcinin gerçek Apple attestation servisinden geçtiğini kanıtlar
Nonce (OID 1.2.840.113635.100.8.2)
authData + clientDataHash'in SHA256'sı
İsteğin sunucunun ürettiği challenge'a bağlı olduğunu doğrular
RP ID hash
App ID'nin SHA256 hash'i
Attestation'ın doğru uygulamaya ait olduğunu doğrular
counter
0 (yalnız ilk attestation'da)
Attestation'ın ilk kullanım olduğunu, klonlanmadığını gösterir
aaguid
appattest / appattestdevelop / appattestsandbox
Ortamı (prod/dev/sandbox) doğrular

Play Integrity Verdict'lerini Yorumlamak

Play Integrity API, tek bir "geçti/kaldı" bayrağı yerine birden fazla verdict alanı döner; backend'in bunları birlikte yorumlaması gerekir. deviceIntegrity alanı cihazın gerçek ve sertifikalı bir Android cihaz olup olmadığını gösterir; MEETS_DEVICE_INTEGRITY değerinin dokümandaki tanımı: "The app is running on a genuine and certified Android device." Android 13 ve sonrasında bu doğrulama, bootloader'ın kilitli olduğuna dair donanım destekli bir kanıtla güçlendirilir. appRecognitionVerdict alanındaki PLAY_RECOGNIZED değeri, uygulamanın imza sertifikasının Google Play'in dağıttığı sürümle eşleştiğini doğrular: "The app and certificate match the versions distributed by Google Play." appLicensingVerdict alanındaki LICENSED değeri ise kullanıcının uygulamayı gerçekten Google Play üzerinden kurduğunu/güncellediğini gösterir: "The user installed or updated your app from Google Play on their device."

Bu üç verdict grubu birbirinden bağımsız değerlendirilmeli: bir cihaz MEETS_DEVICE_INTEGRITY verse bile appLicensingVerdict UNLICENSED dönüyorsa, bu genellikle uygulamanın Play dışı bir kaynaktan (sideload) yüklendiği anlamına gelir — cihaz güvenilir olsa da dağıtım kanalı değil. Aşağıdaki tablo, Play Integrity Standard API'nin temel verdict gruplarını özetliyor.

Verdict Grubu
Örnek Değer
Ne Anlama Gelir
deviceIntegrity
MEETS_DEVICE_INTEGRITY
Cihaz gerçek, sertifikalı, bootloader kilitli
appRecognitionVerdict
PLAY_RECOGNIZED
İmza sertifikası Play dağıtımıyla eşleşiyor
appLicensingVerdict
LICENSED
Uygulama Play üzerinden kurulmuş/güncellenmiş
appLicensingVerdict
UNLICENSED
Uygulama Play dışı bir kaynaktan gelmiş olabilir

Play Integrity Sunucu Tarafı Doğrulama: Standard ve Classic

Play Integrity iki farklı API sunar: Standard (istek bazlı, requestHash ile) ve Classic (nonce bazlı, JWE/JWS iç içe token). Standard API'de requestHash alanı olmadan üretilen bir token yalnızca cihaza bağlanır, spesifik isteğe değil — bu da saldırı riskini açar: "Without the requestHash, the integrity token will be bound only to the device, but not to the specific request, which opens up the possibility of attack." Google Play, aynı token'ın çok kez tekrar kullanılmasını otomatik olarak engeller: "Google Play automatically prevents integrity tokens from being reused many times." Sunucu tarafı doğrulama, playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken uç noktasına POST isteğiyle yapılır.

Classic API'de nonce ve token biçimi

Classic API'de ise nonce URL-safe Base64 ile kodlanmış, 16-500 karakter uzunluğunda olmalı; dokümanın madde listesi: "String, URL-safe, Encoded as Base64 and non-wrapping, Minimum of 16 characters, Maximum of 500 characters." Token, JWE içinde JWS olan iç içe bir yapı taşır (A256KW/A256GCM şifreleme + ES256 imza): "The token is a nested JSON Web Token (JWT), that is JSON Web Encryption (JWE) of JSON Web Signature (JWS)." Önemli bir uyarı: nonce ve requestHash alanlarına koyduğun veri, istemci uygulamana ve Google'a açık metin (cleartext) olarak görünür — bu yüzden bu alanlara doğrudan hassas veri (örneğin ham kullanıcı şifresi) koymamalısın: "Data that you use for the requestHash and nonce fields is visible in cleartext to your app, and to Google."

kotlin
1// Android istemcisi — Play Integrity Standard API isteği
2val standardIntegrityManager = IntegrityManagerFactory.createStandard(applicationContext)
3val requestHash = sha256Base64(requestBodyBytes) // sunucudan gelen isteğin hash'i
4 
5val request = StandardIntegrityManager.PrepareIntegrityTokenRequest.builder()
6 .setCloudProjectNumber(CLOUD_PROJECT_NUMBER)
7 .build()
8 
9standardIntegrityManager.prepareIntegrityToken(request)
10 .addOnSuccessListener { tokenProvider ->
11 val tokenRequest = StandardIntegrityManager.StandardIntegrityTokenRequest.builder()
12 .setRequestHash(requestHash)
13 .build()
14 tokenProvider.request(tokenRequest)
15 .addOnSuccessListener { response ->
16 sendTokenToServer(response.token())
17 }
18 }

İki Platform, Tek Endpoint: Ortak Backend Tasarımı

App Attest ve Play Integrity, protokol düzeyinde tamamen farklı (biri sertifika zinciri doğrulayan bir attestation nesnesi, diğeri Google'ın kendi servisine decode ettirdiğin şifreli bir token) ama backend'de aynı soruya cevap veriyorlar: "bu istek gerçek, değiştirilmemiş bir istemciden mi geldi?" Bu yüzden tek bir verifyDeviceIntegrity(platform, token, expectedChallenge) soyutlaması altında birleştirmek pratik oluyor — servis katmanı platforma göre dallanıyor, ama üst katmandaki iş mantığı (kullanıcı akışı, hata kodları, metrik toplama) ortak kalıyor.

typescript
1type IntegrityVerdict = {
2 trusted: boolean;
3 reason?: string;
4 platform: "ios" | "android";
5};
6 
7async function verifyDeviceIntegrity(
8 platform: "ios" | "android",
9 token: string,
10 expectedChallenge: string,
11): Promise<IntegrityVerdict> {
12 if (platform === "ios") {
13 return verifyAppAttestAssertion(token, expectedChallenge);
14 }
15 return verifyPlayIntegrityToken(token, expectedChallenge);
16}
17 
18// Ortak middleware: her iki platform da aynı hata sözleşmesini kullanır
19app.post("/api/sensitive-action", async (req, res) => {
20 const verdict = await verifyDeviceIntegrity(
21 req.body.platform,
22 req.body.integrityToken,
23 req.session.challenge,
24 );
25 if (!verdict.trusted) {
26 return res
27 .status(403)
28 .json({ error: "device_integrity_failed", reason: verdict.reason });
29 }
30 // iş mantığına devam
31});

Bu tasarımda dikkat etmen gereken nokta, her iki platformun da "challenge" kavramını farklı biçimde taşıması: App Attest'te challenge attestation/assertion'a doğrudan gömülür, Play Integrity Standard'da ise requestHash olarak isteğe bağlanır. İkisini de sunucunun ürettiği, kısa ömürlü, tek kullanımlık bir değerden türetmen gerekiyor — aksi halde ortak soyutlamanın altındaki güvenlik garantisi kaybolur.

Challenge/Nonce ile Replay Koruması

Her iki platformda da replay koruması aynı temel prensibe dayanır: sunucu her doğrulama turu için yeni, tahmin edilemez bir değer üretir; istemci bu değeri kriptografik olarak imzalanmış/şifrelenmiş yapıya gömer; sunucu, dönen sonucun kendi ürettiği değere bağlı olduğunu kontrol eder. Apple bunu App Attest'te hem attestation hem assertion adımında ayrı ayrı uygular — attestation, sunucudan gelen challenge'ın hash'ini gömer; assertion da replay saldırılarını önlemek için yine bir challenge kullanır. Play Integrity'de aynı rolü Standard API'de requestHash, Classic API'de nonce alanı üstlenir.

Pratikte bu, sunucunda kısa ömürlü bir challenge deposu tutman gerektiği anlamına gelir: kullanıcı bir işlem başlatmadan hemen önce GET /api/challenge ile rastgele bir değer alır, bu değeri istemci attestation/token üretiminde kullanır, sunucu doğrulama sırasında bu değerin daha önce kullanılmadığını ve süresinin dolmadığını (örneğin 2-5 dakika) kontrol eder. Challenge'ı kullandıktan hemen sonra depodan silmek (tek kullanımlık invalidasyon), aynı token'ın ikinci kez gönderilmesini engeller — Google'ın "integrity token'ların çok kez tekrar kullanılmasını otomatik engellediği" garantisi, kendi challenge yönetimini atlamana gerekçe olmamalı; iki katman birbirini tamamlar, birbirinin yerine geçmez.

Yanlış Pozitifler ve Kullanıcı Deneyimi

Attestation/integrity doğrulaması, gerçek kullanıcıları yanlışlıkla engellediğinde ürün için maliyetlidir. En sık görülen yanlış pozitif kaynağı, anahtarın geçersiz kaldığı geçiş durumlarıdır: Apple'ın belirttiği gibi App Attest anahtarları normal uygulama güncellemelerinde geçerli kalır ama uygulamanın silinip yeniden kurulmasında, cihaz göçünde veya yedekten geri yüklemede geçerliliğini yitirir. Bu durumda sunucunun kullanıcıyı doğrudan reddetmek yerine "yeniden attestation" akışına yönlendirmesi gerekir — aksi halde meşru bir kullanıcı, telefonunu değiştirdiği için hesabından atılmış gibi hisseder.

Android tarafında da benzer bir dikkat gerekiyor: appLicensingVerdict UNLICENSED dönmesi tek başına "bu kullanıcı kötü niyetli" anlamına gelmez — geliştirici test cihazları, dahili dağıtım kanalları veya bölgesel Play erişim kısıtlamaları da aynı sonucu üretebilir. Bu yüzden verdict'leri sert bir "izin ver/reddet" ikilisi yerine kademeli bir güven skoruna dönüştürmeyi tercih ederim: deviceIntegrity başarısızsa işlemi tamamen engelle, yalnızca appLicensingVerdict şüpheliyse ek bir doğrulama adımı (örneğin e-posta onayı) iste.

  • Anahtar geçersizliği: Yeniden kurulum, cihaz göçü veya restore sonrası eski App Attest anahtarı geçersiz olur — kullanıcıyı reddetme, sessizce yeniden attest et.
  • Şüpheli ama kesin değil: appLicensingVerdict: UNLICENSED tek başına saldırı kanıtı değildir — kademeli tepki ver.
  • Kesin engelleme: deviceIntegrity hiçbir MEETS_* değeri döndürmüyorsa veya App Attest sertifika zinciri doğrulanamıyorsa işlemi durdur.

Kademeli Devreye Alma ve Metrikler

Attestation'ı bir gecede tüm kullanıcı tabanına açmak riskli bir hamledir — hem sunucu tarafı doğrulama kodundaki olası hatalar hem de büyük ölçekte ortaya çıkan edge case'ler, gerçek kullanıcıları engelleyebilir. Apple, büyük kullanıcı tabanına sahip uygulamalar için kademeli ve düzenli bir açılış öneriyor: "we suggest gradually and uniformly ramping up no more than 10 million users per day per app." Bu rakam yalnızca en büyük ölçekli uygulamalar için bir üst sınır niteliğinde olsa da, prensip küçük ölçekli uygulamalar için de geçerli: attestation'ı önce yüzde birkaçlık bir kullanıcı diliminde aç, "doğrulama başarısız" oranını izle, sonra kademeli olarak genişlet.

İzlemen gereken en kritik metrik, doğrulama başarısız oranının platform, uygulama sürümü ve işletim sistemi sürümüne göre kırılımıdır. Tek bir global "başarısızlık oranı" grafiği, belirli bir cihaz ailesinde veya eski bir uygulama sürümünde patlak veren bir sorunu gizler. Ben genelde attestation'ı önce "yalnızca logla, engelleme" modunda devreye alıp gerçek trafikteki başarısızlık dağılımını bir süre gözlemlemeyi tercih ederim — bu, sert engellemeye geçmeden önce kod hatası ile gerçek saldırıyı birbirinden ayırmanı sağlar.

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

Okuyucu Ödülü

Sunucu tarafı attestation doğrulaması kurarken atlanması kolay ama kritik olan kontrol noktalarını tek yerde topladım. Bu listeyi kod incelemesi sırasında bir kontrol listesi gibi kullanabilirsin; her madde, yukarıdaki bölümlerde kaynağıyla birlikte anlatılan bir adıma karşılık geliyor.

SSS

App Attest sunucuda nasıl doğrulanır?

Sunucu önce attestation objesindeki x5c sertifika zincirini Apple'ın kök sertifikasına kadar doğrular, ardından authenticator data'nın sonuna clientDataHash'i ekleyip SHA256 ile bir nonce türetip bunu sertifikanın OID 1.2.840.113635.100.8.2 uzantısındaki değerle karşılaştırır. Sonra App ID'nin hash'ini RP ID hash'iyle, public key'i keyId ile eşleştirir; son olarak counter alanının 0 olduğunu ve aaguid'in çalıştığın ortama (production/development/sandbox) uygun olduğunu kontrol eder.

Play Integrity token'ı backend'de nasıl kontrol edilir?

Standard API'de istemci, sunucudan aldığı isteğin hash'ini requestHash alanına koyarak bir token üretir; bu token playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken uç noktasına POST edilerek çözülür. Dönen yanıttaki deviceIntegrity, appRecognitionVerdict ve appLicensingVerdict alanları ayrı ayrı değerlendirilir; Classic API kullanıyorsan token, JWE içinde JWS olan iç içe bir yapıdır ve nonce alanının 16-500 karakter, URL-safe Base64 olması gerekir.

Attestation jailbreak tespitinden neden daha güvenilir?

Jailbreak/root tespiti, istemci tarafında çalışan ve dolayısıyla değiştirilmiş bir uygulama tarafından atlatılabilecek bir kontroldür — sonucu sunucuya "jailbreak yok" diye yalan söyleyebilir. App Attest ve Play Integrity ise donanım destekli anahtarlar ve platform sağlayıcısının (Apple/Google) imzaladığı bir attestation zinciri kullanır; sonuç istemci kodu tarafından üretilmez, doğrudan işletim sistemi/donanım güvenlik bileşeni tarafından üretilip imzalanır, bu da sahtelenmesini ciddi biçimde zorlaştırır.

App Attest anahtarı ne zaman geçersiz olur?

Apple'a göre üretilen anahtarlar normal uygulama güncellemelerinde geçerliliğini korur ama uygulamanın silinip yeniden kurulmasında, cihaz göçünde veya yedekten geri yüklemede geçersiz hale gelir. Bu durumlarda sunucunun kullanıcıyı reddetmek yerine yeni bir attestation akışı başlatması gerekir.

deviceIntegrity ile appLicensingVerdict farkı nedir?

deviceIntegrity, cihazın gerçek ve sertifikalı bir Android cihaz olup olmadığını (bootloader kilidi dahil) gösterir; appLicensingVerdict ise uygulamanın Google Play üzerinden kurulup kurulmadığını gösterir. Bir cihaz güvenilir (MEETS_DEVICE_INTEGRITY) olabilir ama uygulama Play dışı bir kaynaktan yüklenmiş (UNLICENSED) olabilir — bu iki sinyal birbirinin yerine geçmez.

Güncelleme (Eylül 2026)

Bu yazının gövdesi Kasım 2025 itibarıyla geçerli olan API yüzeyini anlatıyor. Eylül 2026 itibarıyla Apple tarafında App Attest ve DeviceCheck sunucu doğrulama akışında (challenge/assertion/counter/aaguid modeli) resmi dokümanlarda bir kırıcı değişiklik veya duyurulmuş bir geçiş zorunluluğu görünmüyor; temel sözleşme yazı yayınlandığındaki haliyle aynı kalmış görünüyor. Android tarafında ise Play Integrity'nin temel sözleşmesi (Standard/Classic ayrımı, requestHash/nonce mekaniği, decodeIntegrityToken akışı) değişmemiş görünüyor; güncel dokümantasyonda listelenen appAccessRiskVerdict, playProtectVerdict, recentDeviceActivity ve beta aşamasındaki deviceRecall opsiyonel sinyalleri bu yazının gövdesinde kapsanmıyor ama yazı yayımlandığında da mevcuttu. Yayın sonrasındaki tek gerçek değişiklik istemci kütüphanesi tarafında: sürüm notlarına göre 20 Kasım 2025'te yayımlanan 1.6.0 sürümü PrepareIntegrityTokenRequest içine webViewRequestMode parametresini ekledi. Yeni bir entegrasyon planlıyorsan bu opsiyonel alanları eklemeden önce güncel Play Integrity dokümantasyonunu tekrar kontrol etmen, makalede anlatılan temel üç verdict grubunun (deviceIntegrity, appRecognitionVerdict, appLicensingVerdict) hâlâ çekirdek sözleşme olduğunu unutmaman yeterli.

Sonuç

App Attest ve Play Integrity, "bu istek gerçek bir cihazdan mı geldi" sorusuna sunucu tarafında güvenilir bir cevap üretir — ama bu güven, doğrulama adımlarını eksiksiz uyguladığın ve challenge/nonce mekanizmasını doğru işlettiğin sürece geçerlidir. Sertifika zincirini atlarsan, counter kontrolünü unutursan veya challenge'ı kısa ömürlü tutmazsan, sistemin görünürde çalışsa da gerçek koruması ortadan kalkar. Bu iki mekanizmayı, Keychain güvenliği ile hassas verileri korumaktan, ağ trafiği güvenliği ile transport katmanını sağlamlaştırmaya kadar uzanan daha geniş bir savunma derinliği stratejisinin bir parçası olarak düşün; iOS güvenlik en iyi pratikleri yazısı bu parçaların nasıl bir araya geldiğini gösteriyor. Uygulamanın gizlilik beyanlarını da ihmal etme — iOS privacy manifest ve App Tracking ile App Tracking Transparency uyumluluğu yazıları, attestation'ın yanına eklemen gereken diğer zorunlu katmanları anlatıyor. Backend tarafında token doğrulama akışını otomatikleştirmek istersen App Store Connect API otomasyonu yazısına da göz atabilirsin.

Kaynaklar

Etiketler

#app attest#play integrity#ios security#android security#backend#attestation#replay protection
Muhittin Çamdalı

Muhittin Çamdalı

Lead Mobile Engineer

12+ yıllık deneyime sahip Lead Mobile Engineer. Swift, SwiftUI, Kotlin ve Flutter ile iOS, Android ve cross-platform mimarilerde uzman. Performanslı ve kullanıcı dostu mobil uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

İlgili İçerik