Pagine

2013-07-06

Controllo della receipt Mac App Store

Pur non essendo obbligatorio per pubblicare applicazioni sul Mac App Store (MAS), Apple consiglia di effettuare un controllo sulla ricevuta (receipt) per evitare che tramite una semplice copia da un Mac all'altro la nostra applicazione si trovi ad avere 2 copie vendute e 500 utenti!
Si tratta quindi di una sicurezza aggiuntiva per lo sviluppatore (l'utente è già al sicuro: firma del codice e registro degli acquisti sullo Store permettono di identificare lo sviluppatore - che quindi non inserirà codice cattivo - e di essere sicuri che l'app sul proprio Mac è proprio la stessa rilasciata sul MAS).
Allora, meglio procedere!

Ma come si fa a controllare un documento che esisterà solo sul Mac dell'utente dopo l'acquisto e non su quello dello sviluppatore?

Tempo fa Apple forniva un certificato campione per i test, ma ora la procedura è cambiata: una volta terminato lo sviluppo, si aggiunge il codice di controllo, si lancia l'applicazione, il codice nota l'assenza di ricevuta e ne richiede una apposita al MAS. Una volta ottenuta, la usiamo per testare a fondo il codice di controllo.

Bene, lineare, facile a dirsi... non a farsi!
Dopo vari tentativi, tripla lettura della documentazione ufficiale, ricerche su Google... sono arrivato al procedimento corretto, o per lo meno, quello che funziona! Occorre seguire passo passo le seguenti istruzioni.
  • Per prima cosa, andiamo su iTunes Connect, entriamo con le credenziali di sviluppatore e andiamo nella sezione di gestione degli utenti. Vedremo due grandi pulsanti: uno è per gli utenti normali, l'altro per gli utenti di test; entriamo su quest'ultimo e generiamo un utente di test. La creazione è simile a quella di un utente normale: dovremo fornire una mail apposita (deve essere vera, perchè ci verrà richiesto di validarla) con password e tutti i vari dati. Per sicurezza li ho messi uguali al mio utente standard (a parte la mail, ovviamente!). Naturalmente, questo utente è collegato quello di sviluppatore: in questo modo, saranno disponibili tutte le nostre applicazioni registrate.
  • Mentre siamo su iTunes Connect, registriamo la nostra applicazione come se stessimo per farne l'upload; cioé, il sistema deve avere in pancia il bundleID e la versione dell'app su cui stiamo lavorando. Questo è indispensabile se la nostra app è nuova e non è mai stata registrata; se invece abbiamo già una versione precedente registrata, non c'è bisogno di fare nulla: basta usare per le nostre prove la versione registrata
  • Usciamo da iTunes Connect, apriamo l'applicazione App Store e, dal menu Store, usciamo dal nostro utente; così, quando necessario, il sistema ci chiederà utente e password per accedere. Questo è un punto critico: occorre infatti che Mac OS cerchi di chiamare l'AppStore e si trovi disconnesso: solo così potremo usare l'utente di test.
  • Chiudiamo l'App Store e torniamo a Xcode. Inseriamo una bozza del codice che controllerà la presenza della ricevuta. Come, non serve che sia completo: è sufficiente che controlli la presenza della ricevuta e, in caso manchi, effettui un'uscita immediata con il codice exit(173), come dice la documentazione Apple. Anzi, meglio che si tratti di un codice molto semplice: sarà così più facile evitare gli errori e quindi di dover ripetere la procedura.
  • Apriamo i Build Settings del progetto, cerchiamo la voce Code Signing Identity e impostiamola a Mac Developer. Controlliamo che l'impostazione sia sul target della nostra applicazione e non sul progetto. Questa selezione è un punto critico: se ne scegliamo un'altra, non funzionerà! Notiamo che quando faremo l'upload definitivo su iTunesConnect dovremo riportarla a 3rd Party Developer, altrimenti ci verrà segnalato un errore e l'upload non sarà accettato.
  • Verifichiamo nel Info.plist della nostra applicazione che il BundleID (CFBundleIdentifier) e la versione (CFBundleShortVersionString) siano proprio quelli registrati ufficialmente sull'App Store; se non lo sono, non otterremo alcun risultato.
  • Ora dobbiamo esportare l'applicazione, poiché il tutto funziona solo se l'applicazione è lanciata dal Finder, non da Xcode. Quindi eseguiamo un Archive (menu Product) e verremo portati nella schermata Archive dell'Organizer.
  • Selezioniamo l'archivio e clicchiamo sul pulsante Distribute. Selezioniamo Export As - Application, click Ok.

  • Nella scheda successiva (Code Signing Identity) dobbiamo scegliere la firma indicata con Mac Development (di solito è quella selezionata di default). Dimentichiamoci del triangolino giallo che ci comunica che stiamo sbagliando... e clicchiamo Next. Anche questo è un punto critico: un altro tipo di firma non scatenerebbe l'evento corretto, ma ci ritroveremmo solo con un triste messaggio in Console (App not signed), per di più fuorviante.


  • Ci verrà chiesto dove salvare l'app così esportata e su Xcode abbiamo finito.
  • Ora passiamo al Finder: troviamo la nostra app appena esportata e lanciamola! Se abbiamo fatto tutto correttamente, entro qualche secondo ci comparirà il dialogo standard di accesso all'App Store con richiesta di utente (mail) e password. Inseriamo la coppia, usando le credenziali dell'utente di test. Senza che ce ne accorgiamo, il Finder avrà scaricato una ricevuta, l'avrà inserita nel percorso <miaApp.app/Content/_MAS/> e avrà rilanciato l'applicazione. Questa volta, il codice verificherà che la ricevuta esiste, quindi la nostra app partirà normalmente! Infatti, usciamo dall'app, entriamo dal Finder nel pacchetto e vedremo che nel Content è comparsa la cartella _MAS, contenente la ricevuta!
  • Da Finder, prendiamo questa cartella e mettiamola in salvo da qualche parte (anche se poi potremmo scaricarla quante volte vogliamo).
  • Da Xcode, facciamo una compilazione normale (basta dare un Run oppure Build for Running).
  • Da Finder troviamo l'app appena costruita (è al percorso DerivedData/<nomeApp>/Build/Products/Debug), apriamo il bundle e copiamo dentro Content la cartella _MAS: da ora in poi potremo costruire il nostro codice finale per il controllo della ricevuta e ad ogni Run la ricevuta sarà trovata al suo posto, per cui possiamo inserire tutti i controlli che vogliamo!
Attenzione: se per qualche motivo faremo un Clean del progetto, anche la ricevuta sarà eliminata! Ecco il motivo per cui conviene farne una copia in zona sicura, per riprenderla ogni volta che serve (altrimenti dovremo rifare il processo di esportazione e lancio dal Finder).
Un consiglio: Apple fornisce codice solo per il controllo del GUID del Mac (e conviene utilizzarlo così come è, senza modifiche), ma nulla sugli altri controlli. Non è pigrizia da parte di Apple: il controllo della ricevuta è proprio dove un eventuale pirata va a fare reverse engineering per introdurre del codice che salti i controlli. Se tutte le applicazioni usassero lo stesso codice, trovata una soluzione, sarebbero craccabili tutte le applicazioni con quel codice; per cui è consigliabile che ciascuno trovi la propria soluzione.
Per lo stesso motivo, conviene usare codice di Foundation in C puro e non metodi Objective-C, che si portano nel codice compilato una messe di informazioni in chiaro. Volendo si può procedere a offuscare il codice, in modo che anche un reverse engineering diventi difficile. In ogni caso, un pirata motivato e con tempo a disposizione prima o poi riuscirà nel proprio intento...

Informazioni: la documentazione Apple si trova a questo link; come esempi, consiglio Roddi, AlanQuatermain e Sazameki. Colgo l'occasione per ringraziarli tutti e tre: lo studio del loro codice mi ha fatto imparare molto!