macOS uygulamasını lisanslamak: neye emek verilir, ne boşa gider?
Kendi macOS uygulamamı ücretli dağıtmayı planlarken "lisans sistemi" başlığının altında aslında beş ayrı konu olduğunu gördüm: dağıtım güvenliği, lisans biçimi, cihaz bağlama, çevrimdışı politika ve güncelleme. Bu yazı, Eylül 2026 itibarıyla Apple ve ilgili projelerin belgelerine göre çıkardığım özet. Ürün adı, fiyat ve sağlayıcı seçimi gibi kararlar yazının dışında.
Önce beklentiyi doğru kurmak
İkiliyi (binary) yamalayabilen kararlı bir kırıcıyı hiçbir istemci tarafı önlem durdurmaz. Gerçekçi hedef üç katmanlı:
- Dürüst kullanıcıyı dürüst tutmak (kopyalamak zahmetli olsun).
- Deneme istismarını pahalılaştırmak.
- Kırılmış sürümü güncelleme ve sunucudan gelen içerikten mahrum bırakmak.
Emek bu sıraya göre harcanmalı. Üçüncü madde, listedeki en yüksek getirili önlem: kırık sürüm çalışsa bile değer akışı kesiliyor.
Zorunlu temel: imza, hardened runtime, notarization
Apple'ın dağıtım belgeleri bu üçünü netçe tarif ediyor:
- Developer ID Application sertifikasıyla imza ve güvenli zaman damgası (
codesign --timestamp). - Hardened runtime (
--options runtime) veget-task-allowkapalı. Bu, debugger bağlanmasını ve kod enjeksiyonunu sistem düzeyinde engelliyor; elle "debugger tespiti" yazmaya gerek bırakmıyor. notarytoolile noterleme vestaplerile bileti pakete iliştirme. Apple eskialtoolyolunu kullanımdan kaldırdı.
Paketin içindeki her Mach-O dosya (yardımcı araçlar, XPC servisleri, gömülü çerçeveler) ayrı ayrı imzalanmalı. Doğrulama için codesign --verify --deep --strict ve spctl -a -vv yeterli. Entitlement'larda kural sade: hardened runtime istisnalarından hiçbiri kesinlikle gerekmedikçe açılmaz.
Terminal ya da komut satırı araçları çalıştıran bir uygulama için App Sandbox uyumsuz; bu da Mac App Store kanalını dışarıda bırakıyor. Karar, doğrudan Developer ID dağıtımı.
Karartma: neden listede yok
Araştırmanın en net çıktısı buydu. Swift için bilinen açık kaynak karartıcı SwiftShield'ın kendi README'si "üretim uygulamalarında kullanmayın" diyor ve depo yıllardır güncellenmiyor. Ticari LLVM tabanlı karartıcılar ise niş bir ürün için yüksek lisans ücreti ve derleme kırılganlığı demek.
Aynı şekilde boşa emek saydıklarım:
- String şifreleme: ikilide gizlenecek bir sır yok; güvenlik imzada ve sunucuda.
- Elle debugger tespiti: hardened runtime zaten yapıyor; yanlış pozitif riski var.
- TLS sertifika sabitleme: CDN sertifika döndürmesinde kırılıyor; yerine yanıt imzası daha iyi.
- App Attest: Apple'ın belgesine göre Mac'te desteklenmiyor.
Değerli ve ucuz olanlar: Release derlemede sembol şeridi; SecCodeCopySelf + SecCodeCheckValidity ile uygulamanın kendi imzasını Team ID gereksinimine karşı denetlemesi (yamalanıp yeniden imzalanan sürüm açılışta yakalanır); lisans kontrolünü tek noktaya değil birkaç noktaya dağıtmak.
Lisans biçimi: tek algoritma, imzalı kısa ömürlü kira
JWT kütüphanesi yerine tek algoritmalı kompakt bir biçim tercih ettim: ÖNEK.<base64url(payload)>.<base64url(Ed25519 imza)>. Tek algoritma, "algoritma karışıklığı" saldırı sınıfını baştan kapatıyor. İstemcide CryptoKit ile doğrulanıyor; sunucu tarafı Web Crypto API'si Ed25519'u destekliyor.
Payload'da iki ayrı süre var: hakkın asıl bitişi (deneme ya da abonelik sonu) ve kiranın (lease) bitişi. Kira kısa ömürlü; her yenilemede sunucu yeniden imzalıyor ve istemcinin gönderdiği nonce'u içine koyuyor. Böylece:
- Özel anahtar yalnız sunucuda; keygen yazılamıyor.
- Uygulamada iki genel anahtar (etkin + yedek) duruyor, anahtar döndürme sürüm çıkarmadan yapılabiliyor.
- Yerel proxy'nin "geçerli" demesi işe yaramıyor; yanıt imzalı.
Cihaz bağlama ve saat
Lisansın başka Mac'e taşınmasını engellemek için ilk açılışta Secure Enclave üzerinde bir P-256 imza anahtarı üretiliyor; özel anahtar donanımdan çıkmıyor. Sunucu her yenilemede bir challenge veriyor, istemci bunu cihaz anahtarıyla imzalıyor. Kopyalanan belirteç başka cihazda değersiz.
Saat geri alma klasik numara. Çözüm: kararlar sunucu saatiyle veriliyor, istemci Keychain'de "görülen en büyük sunucu saati"ni tutuyor ve yerel saat bunun belirgin gerisindeyse kilitleniyor. Çalışma sırasında geçen süre için duvar saatine değil monoton saate bakılıyor.
Not: Secure Enclave anahtarının sandbox'sız bir Developer ID uygulamasında ek entitlement gerektirmediğini belgede değil prototipte doğrulamak gerekiyor; bu noktayı açık bırakıyorum.
Çevrimdışı politika: iki seçenek
"Doğrulama sunucusu engellenirse uygulama açılmasın" isteği makul; ama iki uygulama biçimi var.
- Katı çevrimiçi: her açılışta yenileme başarısızsa açılma. Uçakta, captive portal'da ya da sunucu kesintisinde herkes aynı anda kilitlenir.
- İmzalı kira + kısa tolerans: yenileme başarısızsa son kiranın bitişine kadar (örneğin 24 saat) çalış, sonra kilitle. Engel tespit edilirse (alan adı loopback ya da özel IP'ye çözülüyor, yanıt imzası geçersiz) hemen kilitle.
İkincisini seçtim. Engelleme senaryosunda aynı sertlikte davranıyor; kazara çevrimdışılığı tolere ediyor ve tek bir DNS kesintisinin tüm müşterileri kilitlemesini önlüyor. Kırıcı iki seçenekte de aynı; ek güvenlik kazancı yok.
Güncelleme kanalı
Sparkle 2, EdDSA imzası ve delta güncelleme ile bu iş için standart. Özel anahtar derleme makinesinde ve çevrimdışı yedekte; genel anahtar SUPublicEDKey olarak pakette. Sparkle imzası ve Apple kod imzası birlikte çalışıyor. Ayrıca:
- Kullanıcı verisi asla uygulama paketinin içine yazılmaz; güncelleme yalnız
.app'i değiştirir. - İmza kimliği (Team ID, bundle ID) değişmez; Keychain öğelerine erişim buna bağlı.
- Ayar biçimi değişirse şema sürümü + ileri göç + göçten önce yedek.
Ek olarak paket içeriği ve güncelleme hakkı sunucu tarafında lisansa bağlı. Yazının başındaki üçüncü katman burada tamamlanıyor.
Özet
Yapılacaklar: Developer ID + hardened runtime + notarization; kendi imzasını denetleme; Ed25519 imzalı kısa ömürlü kira; Secure Enclave cihaz anahtarı; monoton sunucu saati; 24 saat tolerans ve engel tespitinde kilit; Sparkle ile imzalı güncelleme; içeriği sunucu hakkına bağlama.
Yapılmayacaklar: karartma araçları, string şifreleme, elle debugger tespiti, sertifika sabitleme, çok algoritmalı belirteç biçimi.
Kaynaklar
- Apple — Notarizing macOS software before distribution
- Apple — Hardened Runtime
- Apple — Resolving common notarization issues
- Apple — TN3127: Inside Code Signing: Requirements
- Apple — DCAppAttestService.isSupported
- Sparkle — Documentation
- SwiftShield — README
- Cloudflare Workers — Web Crypto
- RFC 8628 — OAuth 2.0 Device Authorization Grant