Sécurisation des données iOS au repos protection des données de l'utilisateur

Il s'agit du premier de trois articles sur la sécurisation des données utilisateur au repos. Dans cet article, nous allons commencer par les bases de la protection des données sur iOS afin que vous puissiez apprendre les meilleures pratiques actuelles en matière de stockage sécurisé des données avec Swift..

Toute application qui enregistre les données de l'utilisateur doit veiller à la sécurité et à la confidentialité de ces données. Comme nous l'avons vu avec les récentes violations de données, le fait de ne pas protéger les données stockées de vos utilisateurs peut avoir des conséquences très graves. Dans ce tutoriel, vous apprendrez les meilleures pratiques pour protéger les données de vos utilisateurs..

Les permissions

Avant de commencer à stocker vos données personnalisées, examinons les données pouvant être partagées par les applications système.. 

Pour de nombreuses versions d'iOS, il a été nécessaire de demander des autorisations d'application pour utiliser et stocker certaines des données privées de l'utilisateur externes à l'application, telles que lors de l'enregistrement et du chargement d'images dans la photothèque. À partir de iOS 10, toute API qui accède aux données privées de l'utilisateur nécessite que vous déclariez cet accès à l'avance dans le projet. info.plist fichier. 

De nombreux frameworks peuvent accéder aux données en dehors de votre application, et chaque framework a une clé de confidentialité correspondante..

  • Partage Bluetooth: NSBluetoothPeripheralUsageDescription
  • Calendrier: NSCalendarsUsageDescription
  • CallKit: NSVoIPUsageDescription
  • Caméra: NSCameraUsageDescription
  • Contacts: NSContactsUsageDescription
  • Santé: NSHealthShareUsageDescription, NSHealthUpdateUsageDescription
  • HomeKit: NSHomeKitUsageDescription
  • Emplacement: NSLocationAlwaysUsageDescription, NSLocationUsageDescription, NSLocationWhenInUseUsageDescription
  • Médiathèque: NSAppleMusicUsageDescription
  • Microphone: NSMicrophoneUsageDescription
  • Mouvement: NSMotionUsageDescription
  • Photos: NSPhotoLibraryUsageDescription
  • Rappels: NSRemindersUsageDescription
  • Reconnaissance de la parole: NSSpeechRecognitionUsageDescription
  • SiriKit: NSSiriUsageDescription
  • Fournisseur de télévision: NSVideoSubscriberAccountUsageDescription

Par exemple, voici une entrée dans info.plist pour permettre à votre application de charger et de stocker des valeurs dans le calendrier.

NSCalendarsUsageDescription Afficher et ajouter des événements à votre calendrier

Si une description d'utilisation est manquante lorsque l'API tente d'accéder aux données, l'application se bloque simplement..

L'API de protection des données

Pour toutes les données utilisateur internes à l'application, la première chose à prendre en compte est de savoir si vous devez stocker les informations et quelles données sont essentielles pour l'application. Conservez autant de ces données essentielles dans la mémoire de travail que dans le stockage de fichiers. Ceci est particulièrement important pour toute information personnellement identifiable. 

Toutefois, si vous devez stocker des données, il est judicieux d'activer la protection des données Apple..

La protection des données chiffre le contenu du conteneur de votre application. Il repose sur le fait que l'utilisateur possède un code d'authentification. La sécurité du cryptage est donc liée à la force du code d'authentification. Avec Touch ID et le chiffrement de système de fichiers mis à niveau introduit dans iOS 10.3, le système de protection des données a fait l'objet de nombreuses améliorations. Vous pouvez activer la protection des données dans votre application en activant Protection des données dans le Les capacités section de votre fichier de projet. Ceci met à jour votre profil d'approvisionnement et votre fichier de droits pour inclure la fonctionnalité de protection des données. La protection des données offre quatre niveaux de protection, décrits par le FileProtectionType structure:

  • aucun: pas de protection.
  • Achevée: les données ne sont pas accessibles lorsque l'appareil est verrouillé. C'est le réglage recommandé pour la plupart des applications.
  • completeUnlessOpen: les données sont accessibles lorsque le terminal est déverrouillé et le restent jusqu'à la fermeture du fichier, même si l'utilisateur verrouille le terminal. Des fichiers peuvent également être créés lorsque le périphérique est verrouillé. Cette option est utile lorsque vous devez ouvrir un fichier à traiter et laisser le processus se poursuivre même si l'utilisateur place l'application en arrière-plan et verrouille l'appareil. Un exemple pourrait être un travail qui télécharge un fichier sur un serveur.
  • completeUntilFirstUserAuthentication: au démarrage du périphérique, les fichiers ne sont accessibles que lorsque l'utilisateur le déverrouille pour la première fois. Après cela, les fichiers sont disponibles même lorsque le périphérique est à nouveau verrouillé. Cette option convient aux fichiers qui doivent être consultés ultérieurement en arrière-plan lorsque le périphérique est verrouillé, par exemple lors d'une tâche d'extraction en arrière-plan..

Achevée est le niveau par défaut. Pour éviter les plantages lorsque votre code tente d'accéder à des données verrouillées, vous pouvez vous inscrire pour recevoir des notifications via UIApplicationProtectedDataDidBecomeAvailable et UIApplicationProtectedDataWillBecome Unavailable pour savoir quand les données sont disponibles.

NotificationCenter.default.addObserver (forName: .UIApplicationProtectedDataDidBecomeAvailable, objet: nil, file d'attente: OperationQueue.main, à l'aide de: (notification) dans //…) NotificationCenter.default.addObserver (forName: .UIApplicationProtectedDataProtectedDataWecome non disponible, objet: nm, OperationQueue.main, en utilisant: (notification) in //…)

En outre, vous pouvez également vérifier la UIApplication.shared.isProtectedDataAvailable drapeau.

Lors de l’activation de la protection des données, il est important de garder à l’esprit que si vous utilisez des services d’arrière-plan tels que la récupération en arrière-plan, ce code peut nécessiter un accès à vos données en arrière-plan lorsque le périphérique est verrouillé. Pour ces fichiers, vous devrez définir un niveau de protection de completeUntilFirstUserAuthentication. Vous pouvez contrôler le niveau de protection de chaque fichier individuellement lors de la création de fichiers et de répertoires à l'aide du Gestionnaire de fichiers classe.

let ok = FileManager.default.createFile (atPath: somePath, contenu: nil, attributs: [FileAttributeKey.protectionKey.rawValue: FileProtectionType.complete]) testez FileManager.default.createDirectory (atPath: somePath, someDirectors: true, attributs: [FileAttributeKey.protectionKey.rawValue: FileProtectionType.complete]) catch print (error)

Vous pouvez également définir le niveau de protection lorsque vous écrivez dans un fichier. le Les données object a une méthode qui peut écrire ses données dans un fichier et vous pouvez définir le niveau de protection lorsque vous appelez cette méthode.

let data = Data.init () let fileURL = try! FileManager.default.url (pour: .documentDirectory, dans: .userDomainMask, appropriéFor: nil, créez: false) .appendingPathComponent ("somedata.dat") do essayer data.write (dans: fileURL, options: ([.atomic , .completeFileProtection])) catch print (error)

Vous pouvez également définir le niveau de protection lors de la configuration de votre modèle Core Data..

let storeURL = docURL? .appendingPathComponent ("Model.sqlite") laisse storeOptions: [AnyHashable: Any] = [NSPersistentStoreFileProtectionKey: FichierProtectionType.complete] do essayez coordinator.addPersistentStore (deType: NSSiteStore, contenu). : storeOptions) catch print (error)

Pour modifier le niveau de protection d'un fichier existant, utilisez les éléments suivants:

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

Intégrité des données

Une partie de la protection de vos données stockées inclut la vérification de son intégrité. Il est recommandé de ne pas faire aveuglément confiance aux données que vous chargez depuis le stockage. il peut avoir été altéré de manière accidentelle ou malveillante. le NSSecureCoding Le protocole peut être utilisé pour charger et sauvegarder en toute sécurité vos objets de données du stockage. Il s'assurera que les objets que vous chargez contiennent les données attendues. Si vous enregistrez votre propre objet, vous pouvez vous conformer au protocole de codage sécurisé de votre classe..

classe ArchiveExample: NSObject, NSSecureCoding var stringExample: String?… 

La classe doit être héritée de NSObject. Ensuite, pour activer le codage sécurisé, remplacez le prend en charge le codage sécurisé méthode du protocole.

static var supportSecureCoding: Bool get return true

Si votre objet personnalisé est désérialisé avec init? (codeur aDecoder: NSCoder), la decodeObject (forKey :) cette méthode doit être remplacée par decodeObject (of: forKey :), qui s'assure que les types d'objet corrects sont décompressés du stockage.

required init? (codeur aDecoder: NSCoder) stringExample = aDecoder.decodeObject (of: NSString.self, forKey: "exemple_chaîne") en tant que chaîne?  func encode (avec aCoder: NSCoder) aCoder.encode (stringExample, forKey: "exemple_chaîne")

Si vous utilisez NSKeyedUnarchiver pour charger des données depuis le stockage, assurez-vous de définir requiert un codage sécurisé propriété.

classe func loadFromSavedData () -> ArchiveExample? objet var: ArchiveExample? = nil let chemin = NSSearchPathForDirectoriesInDomains (.documentDirectory, .userDomainMask, true) [0] en tant que chaîne let url = NSURL (fileURLWithPath: chemin) laissez fileURL = url.appendingPathComponent ("ArchiveExample.plist") si FileManagerfalamental.pend.pagment : (fileURL? .path)!) do let data = try Data.init (contentsOf: fileURL!) let unarchiver = NSKeyedUnarchiver.init (forReadingWith: data) unarchiver.requiresSecureCoding = true objet = un objet d'archivage.decodeObject (de: ArchiveExample) .self, forKey: NSKeyedArchiveRootObjectKey) unarchiver.finishDecoding () catch print (erreur) objet de retour; 

L'activation du codage sécurisé pour vos opérations de sauvegarde vous évitera d'archiver accidentellement un objet qui ne respecte pas le protocole de codage sécurisé..

func save () let path = NSSearchPathForDirectoriesInDomains (.documentDirectory, .userDomainMask, true) [0] en tant que String let url = NSURL (fileURLWithPath: chemin) laissez filePath = url.appendingPathComponent ("ArchiveExample.plist")?. = NSMutableData.init () let archiver = NSKeyedArchiver.init (forWritingWith: data) archiver.requiresSecureCoding = true archiver.encode (self, pour Key: NSKeyedArchiveRootObjectKey) archiver.finishEncoding () ] do essayer data.write (toFile: filePath !, options: options) catch print (error)

Au-delà NSSecureCoding, il est toujours bon d'implémenter vos propres contrôles de validation des données lors de la décompression d'une archive ou de la réception d'une entrée arbitraire en général.

Pistes de données

Alors que iOS continue d'évoluer, de nouvelles fonctionnalités risquent de laisser échapper des données stockées. À partir de iOS 9, vous pouvez indexer votre contenu dans la recherche Spotlight et sur iOS 10, vous pouvez exposer votre contenu à des widgets, tels que le widget Aujourd'hui affiché sur l'écran de verrouillage. Soyez prudent si vous souhaitez exposer votre contenu avec ces nouvelles fonctionnalités. Vous pourriez finir par partager plus que ce que vous aviez prévu!

iOS 10 ajoute également une nouvelle fonctionnalité de transfert dans laquelle vos données de presse-papier copiées sont automatiquement partagées entre les périphériques. Encore une fois, veillez à ne pas exposer à Handoff les données sensibles contenues dans la table de montage. Vous pouvez le faire en marquant le contenu sensible comme localOnly. Vous pouvez également définir une date et une heure d'expiration pour les données..

let stringToCopy = "copie-moi dans le carton" let let pasteboard = UIPasteboard.general si #available (iOS 10, *) let demain = Date (). addedTimeInterval (60 * 60 * 24) pasteboard.setItems ([[kUTTypeUTF8PlainText en tant que String: stringToCopy]], options: [UIPasteboardOption.localOnly: true, UIPasteboardOption.expirationDate: demain]) else pasteboard.string = stringToCopy

Les fichiers enregistrés dans la mémoire de l'appareil peuvent automatiquement être sauvegardés, soit dans iTunes, soit dans iCloud. Même si les sauvegardes peuvent être cryptées, il est judicieux d'exclure tous les fichiers sensibles qui n'ont même pas besoin de quitter l'appareil. Cela peut être fait en réglant la isExcludedFromBackup drapeau sur le fichier.

let path: String =… var url = URL (fileURLWithPath: path) do var resourceValues ​​= URLResourceValues ​​() // ou si vous souhaitez d'abord vérifier l'indicateur: // var resourceValues ​​= try url.resourceValues ​​(forKeys: [.isExcludedFromBackupKey ]) resourceValues.isExcludedFromBackup = true; try url.setResourceValues ​​(resourceValues) catch print (error)

L'animation qui se produit lors de la mise en arrière d'une application est réalisée par iOS en prenant une capture d'écran de votre application, qu'elle utilise ensuite pour l'animation. Lorsque vous consultez la liste des applications ouvertes sur le commutateur d’applications, cette capture d’écran est également utilisée. La capture d'écran est stockée sur l'appareil. 

Il est judicieux de masquer les vues contenant des données sensibles afin que celles-ci ne soient pas capturées dans la capture d'écran. Pour ce faire, configurez une notification lorsque l'application passe en arrière-plan et définissez la propriété masquée pour les éléments d'interface utilisateur à exclure. Ils seront cachés avant qu'IOS ne capture l'écran. Ensuite, lorsque vous arrivez au premier plan, vous pouvez afficher les éléments de l'interface utilisateur..

NotificationCenter.default.addObserver (self, sélecteur: #selector (didEnterBackground), nom: .UIApplicationDidEnterBackground, objet: nil) NotificationCenter.default.addObserver (self, sélecteur: #selector (willEnterForeground), nom: .UIApplicationWillEnterFill, object:

Supprimer vos notifications lorsque la vue disparaît.

NotificationCenter.default.removeObserver (auto, nom: .UIApplicationDidEnterBackground, objet: nil) NotificationCenter.default.removeObserver (auto, nom: .UIApplicationWillEnterForeground, objet: nil)

Votre application dispose également d'un cache clavier pour les champs de texte activés pour la correction automatique. Le texte saisi par l'utilisateur, ainsi que les nouveaux mots appris, sont stockés dans la mémoire cache afin de pouvoir récupérer divers mots que l'utilisateur a saisis précédemment dans votre application. Le seul moyen de désactiver le cache du clavier est de désactiver l'option de correction automatique..

textField.autocorrectionType = UITextAutocorrectionType.no

Vous devez marquer les champs de mot de passe comme une entrée de texte sécurisée. Les champs de texte sécurisés n'affichent pas le mot de passe et n'utilisent pas le cache du clavier.

textField.isSecureTextEntry = true

Les journaux de débogage sont enregistrés dans un fichier et peuvent être récupérés pour les versions de production de votre application. Même lorsque vous codez et déboguez votre application, veillez à ne pas consigner d'informations sensibles telles que les mots de passe et les clés dans la console. Vous pourriez oublier de supprimer ces informations des journaux avant de soumettre votre code à l'App Store! Lors du débogage, il est préférable d'utiliser un point d'arrêt pour afficher les variables sensibles..

Les connexions réseau peuvent également être mises en cache sur le stockage. Pour plus d'informations sur la suppression et la désactivation du cache réseau, consultez l'article Sécurisation des communications sur iOS..

Détruire des données

Vous savez peut-être déjà que lorsqu'un fichier sur un ordinateur est supprimé, le fichier lui-même n'est souvent pas supprimé; seule la référence du fichier est supprimée. Pour supprimer réellement le fichier, vous pouvez écraser le fichier avec des données aléatoires avant de le supprimer.. 

Le passage aux disques à semi-conducteurs a rendu difficile la garantie de la destruction des données, et le meilleur moyen de supprimer les données en toute sécurité est ouvert au débat. Toutefois, ce didacticiel ne serait pas complet sans un exemple montrant comment effacer des données du stockage. En raison de certains autres débats sur l'optimiseur Swift et parce que nous espérons garantir que chaque octet du fichier est effectivement écrasé, nous implémentons cette fonction en C. 

L'implémentation ci-dessous peut aller dans un fichier .c. Pour utiliser la fonction de Swift, vous devez ajouter la définition de la fonction ou le fichier qui contient la fonction dans votre en-tête de pontage. Vous pouvez ensuite appeler cette fonction juste avant les endroits où vous utilisez Gestionnaire de fichiersde effacer le fichier méthodes. Vous voudrez peut-être mettre en œuvre les meilleures pratiques décrites dans ce tutoriel et dans les prochains tutoriels sur une mise à jour d'application. Vous pouvez ensuite effacer les données non protégées précédentes lors de la migration.

#importation  #importation  #importation  #importation  #importation  #importation  #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) // récupère les descripteurs de fichier int result = fstat (fileHandleInt, & stats); if (result == 0) switch (pass) // L'implémentation du DOD 5220.22-M indique que nous écrivons avec trois passages en premier avec 10101010, 01010101 et ensuite avec le troisième cas avec le cas de données aléatoires 1: // avec plus de 10101010 memset (charBuffer, 0x55, sizeof (charBuffer)); Pause; cas 2: // écriture sur 01010101 memset (charBuffer, 0xAA, sizeof (charBuffer)); Pause; cas 3: // écrit avec arc4random pour (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; 

Conclusion

Dans cet article, vous avez appris à définir des autorisations pour les données auxquelles votre application a accès, ainsi qu'à assurer la protection et l'intégrité de base des fichiers. Nous avons également examiné les possibilités de fuite accidentelle des données utilisateur à partir de votre application. Vos utilisateurs vous font confiance pour protéger leurs données. En suivant ces meilleures pratiques, vous pourrez rembourser cette confiance..

Pendant que vous êtes ici, consultez certains de nos autres articles sur le développement d'applications iOS.!