Protezione dei dati iOS a riposo protezione dei dati dell'utente

Questo è il primo di tre articoli sulla protezione dei dati degli utenti a riposo. In questo post, inizieremo con le basi della protezione dei dati su iOS in modo da poter apprendere le attuali migliori pratiche per l'archiviazione sicura dei dati con Swift.

Qualsiasi app che salva i dati dell'utente deve prendersi cura della sicurezza e della privacy di tali dati. Come abbiamo visto con recenti violazioni dei dati, ci possono essere conseguenze molto gravi per la mancata protezione dei dati memorizzati degli utenti. In questo tutorial imparerai alcune best practice per proteggere i dati dei tuoi utenti.

permessi

Prima di archiviare i dati personalizzati, diamo un'occhiata ai dati che possono essere condivisi dalle app di sistema. 

Per molte versioni di iOS, è stato richiesto di richiedere le autorizzazioni delle app per utilizzare e archiviare alcuni dati privati ​​dell'utente esterni all'app, ad esempio quando si salvano e si caricano le immagini nella libreria di foto. A partire da iOS 10, tutte le API che accedono ai dati privati ​​dell'utente richiedono che tu dichiari tale accesso prima del tempo nei tuoi progetti info.plist file. 

Esistono molti framework che possono accedere ai dati al di fuori della tua app e ogni framework ha una chiave di privacy corrispondente.

  • Condivisione Bluetooth: NSBluetoothPeripheralUsageDescription
  • Calendario: NSCalendarsUsageDescription
  • CallKit: NSVoIPUsageDescription
  • Telecamera: NSCameraUsageDescription
  • Contatti: NSContactsUsageDescription
  • Salute: NSHealthShareUsageDescription, NSHealthUpdateUsageDescription
  • HomeKit: NSHomeKitUsageDescription
  • Posizione: NSLocationAlwaysUsageDescription, NSLocationUsageDescription, NSLocationWhenInUseUsageDescription
  • Libreria multimediale: NSAppleMusicUsageDescription
  • Microfono: NSMicrophoneUsageDescription
  • Movimento: NSMotionUsageDescription
  • Fotografie: NSPhotoLibraryUsageDescription
  • promemoria: NSRemindersUsageDescription
  • Riconoscimento vocale: NSSpeechRecognitionUsageDescription
  • Sirikit: NSSiriUsageDescription
  • Provider TV: NSVideoSubscriberAccountUsageDescription

Ad esempio, ecco una voce in info.plist per consentire alla tua app di caricare e memorizzare valori nel calendario.

NSCalendarsUsageDescription Visualizza e aggiungi eventi al tuo calendario

Se manca una descrizione di utilizzo quando l'API tenta di accedere ai dati, l'app si bloccherà semplicemente.

L'API di protezione dei dati

Per qualsiasi dato utente interno all'app, la prima cosa da considerare è se è necessario archiviare le informazioni e quali dati sono essenziali per l'app. Conserva tanto di quei dati essenziali nella memoria di lavoro anziché nella memorizzazione dei file. Questo è particolarmente importante per qualsiasi informazione di identificazione personale. 

Tuttavia, se è necessario memorizzare i dati, è consigliabile abilitare la protezione dei dati di Apple.

Data Protection crittografa il contenuto del contenitore dell'app. Si basa sull'utente che ha un passcode, e quindi la sicurezza della crittografia è legata alla forza del passcode. Con Touch ID e la crittografia del file system aggiornata introdotta in iOS 10.3, il sistema di protezione dei dati ha subito numerosi miglioramenti. Puoi attivare la protezione dei dati sull'app accendendo Protezione dati nel funzionalità sezione del tuo file di progetto. Aggiorna il profilo di provisioning e il file delle autorizzazioni per includere la funzionalità di protezione dei dati. Data Protection offre quattro livelli di protezione, rappresentati da FileProtectionType struttura:

  • nessuna: nessuna protezione.
  • completare: i dati non sono accessibili mentre il dispositivo è bloccato. Questa è l'impostazione consigliata per la maggior parte delle applicazioni.
  • completeUnlessOpen: i dati sono accessibili quando il dispositivo è sbloccato e continuano ad essere accessibili fino alla chiusura del file, anche se l'utente blocca il dispositivo. I file possono anche essere creati quando il dispositivo è bloccato. Questa opzione è utile quando è necessario aprire un file per elaborarlo e continuare il processo anche se l'utente mette l'app in background e blocca il dispositivo. Un esempio potrebbe essere un lavoro che carica un file su un server.
  • completeUntilFirstUserAuthentication: all'avvio del dispositivo, i file non sono accessibili fino a quando l'utente non sblocca prima il dispositivo. Successivamente, i file sono disponibili anche quando il dispositivo è di nuovo bloccato. L'opzione è utile per i file che devono essere accessibili in un secondo momento in background quando il dispositivo è bloccato, ad esempio durante un lavoro di recupero in background.

completare è il livello predefinito. Per evitare arresti anomali quando il codice tenta di accedere ai dati bloccati, è possibile registrarsi per le notifiche tramite UIApplicationProtectedDataDidBecomeAvailable e UIApplicationProtectedDataWillBecomeUnavailable per scoprire quando i dati sono disponibili.

NotificationCenter.default.addObserver (forName: .UIApplicationProtectedDataDidBecomeAvailable, object: nil, queue: OperationQueue.main, using: (notification) in // ...) NotificationCenter.default.addObserver (forName: .UIApplicationProtectedDataWillBecomeUnavailable, object: nil, queue: OperationQueue.main, using: (notification) in // ...)

Inoltre, puoi anche controllare il UIApplication.shared.isProtectedDataAvailable bandiera.

Una cosa importante da tenere a mente quando si abilita la protezione dei dati è che se si utilizzano servizi di background come il recupero in background, quel codice potrebbe aver bisogno di accedere ai propri dati in background quando il dispositivo è bloccato. Per questi file, è necessario impostare un livello di protezione di completeUntilFirstUserAuthentication. È possibile controllare singolarmente il livello di protezione di ciascun file durante la creazione di file e directory utilizzando FileManager classe.

let ok = FileManager.default.createFile (atPath: somePath, contents: nil, attributes: [FileAttributeKey.protectionKey.rawValue: FileProtectionType.complete]) do try FileManager.default.createDirectory (atPath: somePath, withIntermediateDirectories: true, attributes: [FileAttributeKey.protectionKey.rawValue: FileProtectionType.complete]) catch print (errore)

È anche possibile impostare il livello di protezione quando si scrive su un file. Il Dati l'oggetto ha un metodo che può scrivere i suoi dati in un file e puoi impostare il livello di protezione quando chiami questo metodo.

let data = Data.init () lascia fileURL = prova! FileManager.default.url (per: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) .appendingPathComponent ("somedata.dat") do try data.write (to: fileURL, options: ([.atomic , .completeFileProtection])) catch print (errore)

È inoltre possibile impostare il livello di protezione quando si imposta il modello di dati di base.

let storeURL = docURL? .appendingPathComponent ("Model.sqlite") let storeOptions: [AnyHashable: Any] = [NSPersistentStoreFileProtectionKey: FileProtectionType.complete] do try coordinator.addPersistentStore (ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, opzioni : storeOptions) catch print (errore)

Per modificare il livello di protezione di un file esistente, utilizzare quanto segue:

do try FileManager.default.setAttributes ([FileAttributeKey.protectionKey: FileProtectionType.complete], ofItemAtPath: path) catch print (errore)

L'integrità dei dati

Parte della protezione dei dati archiviati include il controllo dell'integrità. È buona norma non fidarsi ciecamente dei dati che si stanno caricando dallo storage; potrebbe essere stato alterato accidentalmente o intenzionalmente. Il NSSecureCoding il protocollo può essere utilizzato per caricare e salvare in modo sicuro gli oggetti dati dalla memoria. Si assicurerà che gli oggetti caricati contengano i dati previsti. Se si salva il proprio oggetto, è possibile conformarsi al protocollo di codifica sicuro all'interno della classe.

class ArchiveExample: NSObject, NSSecureCoding var stringExample: String? ... 

Da cui la classe deve essere ereditata NSObject. Quindi, per attivare la codifica sicura, eseguire l'override di supportsSecureCoding metodo di protocollo.

static var supportsSecureCoding: Bool get return true

Se il tuo oggetto personalizzato è deserializzato con init? (coder aDecoder: NSCoder), il decodeObject (Forkey :) il metodo dovrebbe essere sostituito con decodeObject (di: Forkey :), che si assicura che i tipi di oggetto corretti vengano decompressi dalla memoria.

richiesto init? (coder aDecoder: NSCoder) stringExample = aDecoder.decodeObject (di: NSString.self, forKey: "string_example") come stringa?  func encode (con aCoder: NSCoder) aCoder.encode (stringExample, forKey: "string_example")

Se stai usando NSKeyedUnarchiver per caricare i dati dalla memoria, assicurati di impostarne la sua requiresSecureCoding proprietà.

class func loadFromSavedData () -> ArchiveExample? var object: ArchiveExample? = nil let path = NSSearchPathForDirectoriesInDomains (.documentDirectory, .userDomainMask, true) [0] come String let url = NSURL (fileURLWithPath: percorso) let fileURL = url.appendingPathComponent ("ArchiveExample.plist") se FileManager.default.fileExists (atPath : (fileURL? .path)!) do let data = prova Data.init (contentsOf: fileURL!) let unarchiver = NSKeyedUnarchiver.init (forReadingWith: data) unarchiver.requiresSecureCoding = true object = unarchiver.decodeObject (di: ArchiveExample .self, forKey: NSKeyedArchiveRootObjectKey) unarchiver.finishDecoding () catch print (error) restituisce oggetto; 

L'attivazione della codifica sicura per le tue operazioni di salvataggio ti impedirà di archiviare accidentalmente un oggetto che non aderisce al protocollo di codifica sicuro.

func save () let path = NSSearchPathForDirectoriesInDomains (.documentDirectory, .userDomainMask, true) [0] come String let url = NSURL (fileURLWithPath: path) lascia filePath = url.appendingPathComponent ("ArchiveExample.plist") ?. path lascia dati = NSMutableData.init () let archiver = NSKeyedArchiver.init (forWritingWith: data) archiver.requiresSecureCoding = true archiver.encode (self, forKey: NSKeyedArchiveRootObjectKey) archiver.finishEncoding () let opzioni: NSData.WritingOptions = [.atomic, .completeFileProtection ] do try data.write (toFile: filePath !, opzioni: opzioni) catch print (errore)

Al di là NSSecureCoding, è sempre bene implementare i propri controlli di convalida dei dati dopo aver disimballato qualsiasi archivio o aver ricevuto input arbitrari in generale.

Percorsi dati

Mentre iOS continua ad evolversi, ci sono sempre nuove funzionalità che hanno il potenziale di perdita di dati memorizzati. A partire da iOS 9, i tuoi contenuti possono essere indicizzati nella ricerca Spotlight e su iOS 10 puoi esporre i tuoi contenuti a Widget come il widget Today che appare nella schermata di blocco. Fai attenzione se vuoi esporre i tuoi contenuti con queste nuove funzionalità. Potresti finire per condividere più di quanto avevi programmato!

iOS 10 aggiunge anche una nuova funzione Handoff in cui i dati del pasteboard copiati vengono condivisi automaticamente tra i dispositivi. Anche in questo caso, fare attenzione a non esporre i dati sensibili nel pannello di copia a Handoff. Puoi farlo contrassegnando il contenuto sensibile come localOnly. È inoltre possibile impostare una data e un'ora di scadenza per i dati.

let stringToCopy = "copiarmi su pasteboard" lasciare pasteboard = UIPasteboard.general se #available (iOS 10, *) let tomorrow = Date (). addingTimeInterval (60 * 60 * 24) pasteboard.setItems ([[KUTTypeUTF8PlainText as String: stringToCopy]], opzioni: [UIPasteboardOption.localOnly: true, UIPasteboardOption.expirationDate: tomorrow]) else pasteboard.string = stringToCopy

I file salvati nella memoria del dispositivo possono essere automaticamente sottoposti a backup, sia su iTunes che su iCloud. Anche se i backup possono essere crittografati, è una buona idea escludere qualsiasi file sensibile che non ha nemmeno bisogno di lasciare il dispositivo. Questo può essere fatto impostando il isExcludedFromBackup bandiera sul file.

let percorso: String = ... var url = URL (fileURLWithPath: percorso) do var resourceValues ​​= URLResourceValues ​​() // o se si desidera controllare prima il flag: // var resourceValues ​​= provare url.resourceValues ​​(forKeys: [.isExcludedFromBackupKey ]) resourceValues.isExcludedFromBackup = true; prova url.setResourceValues ​​(resourceValues) catch print (errore)

L'animazione che si verifica quando si posiziona un'app sullo sfondo viene raggiunta da iOS con uno screenshot della propria app che viene quindi utilizzato per l'animazione. Quando guardi l'elenco delle app aperte sullo switcher dell'app, anche questa schermata viene utilizzata. Lo screenshot viene memorizzato sul dispositivo. 

È una buona idea nascondere le viste che rivelano i dati sensibili in modo che i dati non vengano catturati nello screenshot. Per fare ciò, configura una notifica quando l'applicazione sta andando in background e imposta la proprietà nascosta per gli elementi dell'interfaccia utente che desideri escludere. Saranno nascosti prima che iOS catturi lo schermo. Quindi, quando arrivano in primo piano, puoi mostrare gli elementi dell'interfaccia utente.

NotificationCenter.default.addObserver (self, selector: #selector (didEnterBackground), nome: .UIApplicationDidEnterBackground, object: nil) NotificationCenter.default.addObserver (self, selector: #selector (willEnterForeground), name: .UIApplicationWillEnterForeground, object: nil)

Rimuovi le notifiche quando la vista scompare.

NotificationCenter.default.removeObserver (self, nome: .UIApplicationDidEnterBackground, object: nil) NotificationCenter.default.removeObserver (self, nome: .UIApplicationWillEnterForeground, object: nil)

La tua app ha anche una cache della tastiera per i campi di testo con correzione automatica attivata. Il testo che l'utente digita, insieme alle parole appena apprese, sono memorizzati nella cache in modo che sia possibile recuperare varie parole che l'utente ha precedentemente inserito nella propria applicazione. L'unico modo per disattivare la cache della tastiera è disattivare l'opzione di correzione automatica.

textField.autocorrectionType = UITextAutocorrectionType.no

È necessario contrassegnare i campi della password come voce di testo protetta. I campi di testo protetti non visualizzano la password o utilizzano la cache della tastiera.

textField.isSecureTextEntry = true

I registri di debug vengono salvati in un file e possono essere recuperati per le build di produzione della tua app. Anche durante la codifica e il debug della tua app, assicurati di non registrare informazioni sensibili come password e chiavi sulla console. Potresti dimenticare di rimuovere tali informazioni dai log prima di inviare il tuo codice all'app store! Durante il debug, è più sicuro utilizzare un breakpoint per visualizzare le variabili sensibili.

Le connessioni di rete possono anche essere memorizzate nella cache. Ulteriori informazioni sulla rimozione e disattivazione della cache di rete sono disponibili nell'articolo Protezione delle comunicazioni su iOS.

Distruggere i dati

Potresti già sapere che quando un file su un computer viene cancellato, spesso il file stesso non viene rimosso; viene rimosso solo il riferimento per il file. Per rimuovere effettivamente il file, è possibile sovrascrivere il file con dati casuali prima di rimuoverlo. 

Il passaggio alle unità a stato solido ha reso difficile garantire che i dati siano stati distrutti e il modo migliore per eliminare in modo sicuro i dati è aperto al dibattito. Tuttavia, questo tutorial non sarebbe completo senza un esempio di come cancellare i dati dalla memoria. A causa di altri dibattiti sullo swift optimizer e perché speriamo di garantire che ogni byte del file venga sovrascritto, stiamo implementando questa funzione in C. 

L'implementazione sotto può andare all'interno di un file .c. Sarà necessario aggiungere la definizione della funzione o il file che contiene la funzione nell'intestazione del bridging per utilizzare la funzione da Swift. Potresti quindi chiamare questa funzione proprio prima dei luoghi in cui utilizzi FileManager'S Rimuovi il file metodi. Forse potresti voler implementare le migliori pratiche descritte in questo e le prossime esercitazioni su un aggiornamento dell'app. È quindi possibile cancellare i dati non protetti precedenti durante la migrazione.

#importare  #importare  #importare  #importare  #importare  #importare  #define MY_MIN (a, b) (((a) < (b)) ? (a) : (b)) int SecureWipeFile(const char *filePath)  int lastStatus = -1; for (int pass = 1; pass < 4; pass++)  //setup local vars int fileHandleInt = open(filePath, O_RDWR); struct stat stats; unsigned char charBuffer[1024]; //if can open file if (fileHandleInt >= 0) // ottieni i descrittori di file int result = fstat (fileHandleInt, & stats); if (result == 0) switch (pass) // L'implementazione di DOD 5220.22-M afferma che scriviamo sopra con tre passaggi prima con 10101010, 01010101 e poi il terzo con caso di dati casuali 1: // write over con 10101010 memset (charBuffer, 0x55, sizeof (charBuffer)); rompere; caso 2: // write over con 01010101 memset (charBuffer, 0xAA, sizeof (charBuffer)); rompere; caso 3: // write over con arc4random per (unsigned long i = 0; i < sizeof(charBuffer); ++i)  charBuffer[i] = arc4random() % 255;  break; default: //at least write over with random data for (unsigned long i = 0; i < sizeof(charBuffer); ++i)  charBuffer[i] = arc4random() % 255;  break;  //get file size in bytes off_t fileSizeInBytes = stats.st_size; //rewrite every byte of the file ssize_t numberOfBytesWritten; for ( ; fileSizeInBytes; fileSizeInBytes -= numberOfBytesWritten)  //write bytes from the buffer into the file numberOfBytesWritten = write(fileHandleInt, charBuffer, MY_MIN((size_t)fileSizeInBytes, sizeof(charBuffer)));  //close the file lastStatus = close(fileHandleInt);    return lastStatus; 

Conclusione

In questo articolo, hai imparato a impostare le autorizzazioni per i dati a cui la tua app ha accesso, oltre a come garantire la protezione e l'integrità dei file di base. Abbiamo anche esaminato alcuni modi in cui i dati dell'utente potrebbero essere trapelati accidentalmente dalla tua app. I tuoi utenti si fidano di te per proteggere i loro dati. Seguire queste migliori pratiche ti aiuterà a ripagare questa fiducia.

Mentre sei qui, dai uno sguardo ad alcuni dei nostri altri post sullo sviluppo di app per iOS!