De la réduction de l'utilisation du pointeur à la vérification de type forte lors de la compilation, Swift est un excellent langage pour le développement sécurisé. Mais cela signifie qu'il est tentant d'oublier complètement la sécurité. Il y a toujours des vulnérabilités et Swift séduit également les nouveaux développeurs qui n'ont pas encore appris la sécurité..
Ce tutoriel est un guide de codage sécurisé qui traitera des modifications de Swift 4 ainsi que des nouvelles options d’outils disponibles dans Xcode 9 qui vous aideront à réduire les vulnérabilités de sécurité..
De nombreuses vulnérabilités de sécurité ont tourné autour de C et de son utilisation de pointeurs. En effet, les pointeurs vous permettent d'accéder aux emplacements de mémoire brute, ce qui facilite la lecture et l'écriture dans la mauvaise zone. C’est un moyen important pour les attaquants de modifier de manière malveillante un programme..
Swift supprime principalement les pointeurs, mais il vous permet néanmoins de vous interfacer avec C. De nombreuses API, y compris l'intégralité de l'API Core Foundation d'Apple, sont entièrement basées sur le langage C. Il est donc très facile de réintroduire l'utilisation de pointeurs dans Swift..
Heureusement, Apple a nommé les types de pointeurs de manière appropriée: UnsafePointer
, UnsafeRawPointer
, UnsafeBufferPointer
, et UnsafeRawBufferPointer
. Il arrivera un moment où l'API avec laquelle vous vous connectez renverra ces types, et la règle principale lors de leur utilisation est ne pas stocker ou renvoyer des pointeurs pour une utilisation ultérieure. Par exemple:
let myString = "Bonjour le monde!" var unsafePointer: UnsafePointer? = nil myString.withCString myStringPointer dans unsafePointer = myStringPointer // un jour plus tard… print (unsafePointer? .pointee)
Comme nous avons accédé au pointeur en dehors de la fermeture, nous ne savons pas avec certitude s'il pointe toujours sur le contenu de la mémoire attendue. Le moyen le plus sûr d’utiliser le pointeur dans cet exemple serait de le conserver, avec la déclaration print, dans la fermeture..
Les pointeurs vers les chaînes et les tableaux n'ont également pas de vérification des limites. Cela signifie qu'il est facile d'utiliser un pointeur non sécurisé sur un tableau, mais d'accéder accidentellement au-delà de sa limite - un débordement de mémoire tampon.
var numbers = [1, 2, 3, 4, 5] numbers.withUnsafeMutableBufferPointer tampon dans // tampon ok [0] = 5 impression (tampon [0]) // tampon incorrect [5] = 0 impression (tampon [5 ])
La bonne nouvelle est que Swift 4 tente de bloquer l'application au lieu de continuer avec ce que l'on appellerait comportement indéfini. On ne sait pas quoi tampon [5]
pointe vers! Cependant, Swift ne va pas attraper tous les cas. Définissez un point d'arrêt après le code suivant et examinez les variables une
et c
. Ils seront mis à 999
.
func getAddress (pointeur: UnsafeMutablePointer) -> UnsafeMutablePointer return pointer var a = 111 var b = 222 var c = 333 let pointeur: UnsafeMutablePointer = getAddress (pointeur: & b) pointer.successor (). initialize (à: 999) pointer.predecessor (). initialize (à: 999)
Cela démontre une débordement de pile car sans affectation explicite, les variables sont généralement stockées dans la pile.
Dans l'exemple suivant, nous faisons une allocation avec une capacité de seulement un Int8
. Les allocations étant stockées sur le tas, la ligne suivante débordera du tas. Pour cet exemple, Xcode vous avertit uniquement avec une note dans la console qui obtient
est dangereux.
let buffer = UnsafeMutablePointer.allocate (capacité: 1) obtient (tampon)
Alors, quel est le meilleur moyen d'éviter les débordements? Il est extrêmement important, lors de l’interfaçage avec C, de vérifier les limites de l’entrée pour s’assurer qu’elle se trouve à portée..
Vous pensez peut-être qu'il est assez difficile de se souvenir et de trouver tous les cas. Donc, pour vous aider, Xcode est livré avec un outil très utile appelé Address Sanitizer.
Address Sanitizer a été amélioré dans Xcode 9. C’est un outil qui vous aide à détecter les accès non valides à la mémoire, tels que les exemples que nous venons de voir. Si vous travaillerez avec le Peu sûr*
types, c’est une bonne idée d’utiliser l’outil Address Sanitizer. Il n’est pas activé par défaut. Pour l’activer, allez à Produit> Schéma> Éditer un schéma> Diagnostics, et vérifie Adresse Désinfectant. Dans Xcode 9 il y a une nouvelle sous-option, Détecter l'utilisation de la pile après le retour. Cette nouvelle option détecte les vulnérabilités d'utilisation après parcelle et d'utilisation après retour de notre premier exemple..
Parfois négligé est le débordement d'entier. En effet, les débordements d'entiers ne sont des failles de sécurité que lorsqu'ils sont utilisés comme index ou taille de mémoire tampon, ou si la valeur inattendue du débordement modifie le flux de code de sécurité critique. Swift 4 détecte les débordements entiers les plus évidents au moment de la compilation, par exemple lorsque le nombre est nettement supérieur à la valeur maximale de l'entier.
Par exemple, ce qui suit ne compilera pas.
var someInteger: CInt = 2147483647 someInteger + = 1
Mais souvent, le nombre arrive dynamiquement au moment de l'exécution, par exemple lorsqu'un utilisateur entre des informations dans un fichier. UITextField
. Comportement non défini Sanitizer est un nouvel outil de Xcode 9 qui détecte le débordement d'entier signé et d'autres bogues de type incompatibilité. Pour l'activer, allez à Produit> Schéma> Éditer un schéma> Diagnostics, et allumer Désinfectant à comportement non défini. Puis dans Paramètres de construction> Assainisseur de comportement non défini, ensemble Activer les vérifications d'entiers supplémentaires à Oui.
Il y a une autre chose à mentionner à propos du comportement indéfini. Même si Swift pur cache des pointeurs, des références et des copies de mémoires tampons sont toujours utilisées en arrière-plan, il est donc possible de rencontrer un comportement auquel vous ne vous attendiez pas. Par exemple, lorsque vous commencez à parcourir des index de collection, vous pouvez les modifier accidentellement lors de l'itération..
var numbers = [1, 2, 3] pour le nombre dans les nombres print (number) numbers = [4, 5, 6] //<- accident ??? for number in numbers print(number)
Ici nous venons de causer le Nombres
array pour pointer vers un nouveau tableau dans la boucle. Alors qu'est-ce que nombre
pointer vers? Cela s'appellerait normalement une référence pendante, mais dans ce cas, Swift crée implicitement une référence à une copie du tampon de votre tableau pour la durée de la boucle. Cela signifie que le relevé d’impression imprimera 1, 2 et 3 au lieu de 1, 4, 5… C’est bien! Swift vous évite un comportement indéfini ou un blocage d'application, bien que vous ne vous attendiez peut-être pas non plus à cette sortie. Vos pairs développeurs ne s'attendent pas à ce que votre collection soit mutée au cours de l'énumération. Par conséquent, soyez particulièrement prudent, lors de l'énumération, que vous ne modifiez pas la collection..
Swift 4 a donc une grande application de la sécurité au moment de la compilation pour attraper ces vulnérabilités de sécurité. Il existe de nombreuses situations dans lesquelles la vulnérabilité n'existe pas jusqu'au moment de l'exécution, lorsqu'il y a interaction de l'utilisateur. Swift inclut également une vérification dynamique, qui peut également résoudre bon nombre de problèmes au moment de l'exécution, mais elle est trop coûteuse à effectuer sur plusieurs threads et n'est donc pas effectuée pour le code multithread. Le contrôle dynamique détecte de nombreuses violations, mais pas toutes. Il est donc important d'écrire du code sécurisé en premier lieu.!
Cela dit, passons à un autre domaine très commun pour les attaques par injection de vulnérabilités.
Les attaques par format sont utilisées lorsqu'une chaîne d'entrée est analysée dans votre application en tant que commande non souhaitée. Bien que les chaînes Swift pures ne soient pas sujettes aux attaques par format, le Objective-C NSString
et Fondation de base CFString
les classes sont, et ils sont disponibles à partir de Swift. Ces deux classes ont des méthodes telles que stringWithFormat
.
Disons que l'utilisateur peut entrer du texte arbitraire à partir d'un UITextField
.
let inputString = "Chaîne d'un champ de texte% @% d% p% ld% @% @" en tant que NSString
Cela pourrait être un trou de sécurité si la chaîne de format est gérée directement.
let textFieldString = NSString.init (format: inputString) // bad let textFieldString = NSString.init (format: "% @", inputString) // bon
Swift 4 essaie de gérer les arguments de chaîne de format manquants en renvoyant 0 ou NULL, mais il est particulièrement inquiétant de savoir si la chaîne sera retransmise au runtime Objective-C..
NSLog (textFieldString); // bad NSLog ("% @", textFieldString); //bien
Bien que la plupart du temps, une méthode incorrecte provoque juste un crash, un attaquant peut créer avec soin une chaîne de format permettant d'écrire des données dans des emplacements de mémoire spécifiques de la pile, afin de modifier le comportement de votre application (modification d'un est authentifié
variable).
Un autre grand coupable est NSPredicate
, qui peut accepter une chaîne de format utilisée pour spécifier les données extraites de Core Data. Des clauses telles que COMME
et CONTIENT
permettre les caractères génériques et doit être évité, ou du moins utilisé uniquement pour les recherches. L'idée est d'éviter l'énumération des comptes, par exemple, lorsque l'attaquant entre "a *" comme nom de compte. Si vous changez le COMME
clause à ==
, cela signifie que la chaîne doit littéralement correspondre à «a *».
D'autres attaques courantes se produisent en terminant la chaîne d'entrée plus tôt avec un caractère guillemet simple afin que des commandes supplémentaires puissent être entrées. Par exemple, un identifiant peut être ignoré en entrant ') OU 1 = 1 OU (mot de passe LIKE' *
dans le UITextField
. Cette ligne se traduit par "où mot de passe est comme n'importe quoi", ce qui contourne complètement l'authentification. La solution consiste à échapper complètement à toute tentative d'injection en ajoutant vos propres guillemets doubles dans le code. Ainsi, les guillemets supplémentaires de l'utilisateur sont considérés comme faisant partie de la chaîne d'entrée au lieu d'être un caractère de fin spécial:
let query = NSPredicate.init (format: "mot de passe == \"% @ \ "", nom)
Une autre façon de se protéger contre ces attaques consiste simplement à rechercher et à exclure des caractères spécifiques qui pourraient être dangereux pour la chaîne. Les exemples incluent des citations, ou même des points et des barres obliques. Par exemple, il est possible de faire un attaque de traversée de répertoire quand l'entrée est passée directement au Gestionnaire de fichiers
classe. Dans cet exemple, l'utilisateur entre "… /" pour afficher le répertoire parent du chemin au lieu du sous-répertoire prévu..
let userControllerString = "… /" comme NSString let sourcePath = NSString.init (format: "% @ /% @", Bundle.main.resourcePath!, userControllerString) NSLog ("% @", sourcePath) // au lieu de Build / Products / Debug / Swift4.app / Table des matières / Ressources, ce sera Build / Products / Debug / Swift4.app / Table laisser filemanager: FileManager = FileManager () laisser fichiers = filemanager.enumerator (atPath: sourcePath en tant que chaîne) tout en laissant le fichier = fichiers? .nextObject () print (fichier)
D'autres caractères spéciaux peuvent inclure un octet de fin NULL si la chaîne est utilisée en tant que chaîne C. Les pointeurs vers les chaînes C nécessitent un octet de fin NULL. De ce fait, il est possible de manipuler la chaîne simplement en introduisant un octet NULL. L’attaquant peut vouloir terminer la chaîne plus tôt s’il existe un indicateur tel que needs_auth = 1
, ou lorsque l'accès est activé par défaut et désactivé explicitement, comme avec is_subscriber = 0
.
let userInputString = "nomutilisateur = Ralph \ 0" en tant que NSString let commandString = NSString.init (format: "abonné_utilisateur:% @ & needs_authorization = 1", utilisateurInputString) NSLog ("% s", commandString.utf8String!) // affiche abonné: nom d'utilisateur = Ralph au lieu de subscribe_user: nom d'utilisateur = Ralph & needs_authorization = 1
L'analyse des chaînes HTML, XML et JSON nécessite également une attention particulière. Le moyen le plus sûr de travailler avec eux consiste à utiliser les bibliothèques natives de Foundation qui fournissent des objets pour chaque noeud, tels que le NSXMLParser
classe. Swift 4 introduit la sérialisation conforme aux types à des formats externes tels que JSON. Mais si vous lisez XML ou HTML à l'aide d'un système personnalisé, assurez-vous que les caractères spéciaux de l'entrée utilisateur ne peuvent pas être utilisés pour indiquer à l'interpréteur..
<
doit devenir & lt
.>
devrait être remplacé par & gt
.Et
devraient devenir & amp
.“
ou '
besoin de devenir & quot
et & apos
, respectivement.Voici un exemple de méthode rapide pour supprimer ou remplacer des caractères spécifiques:
var myString = "chaîne à assainir;" myString = myString.replacingOccurrences (de: ";", avec: "")
Un dernier domaine pour les attaques par injection concerne les gestionnaires d’URL. Vérifiez que les entrées de l'utilisateur ne sont pas utilisées directement dans les gestionnaires d'URL personnalisés. ouvrir le lien
et didReceiveRemoteNotification
. Vérifiez que l'URL correspond à vos attentes et qu'elle ne permet pas à un utilisateur de saisir arbitrairement des informations pour manipuler votre logique. Par exemple, au lieu de laisser l’utilisateur choisir l’écran de la pile sur lequel naviguer par index, autorisez uniquement des écrans spécifiques utilisant un identificateur opaque, tel que t = es84jg5urw
.
Si vous utilisez WKWebView
Dans votre application, il peut être utile de vérifier également les URL qui y seront chargées. Vous pouvez remplacer decidePolicyPor navigationAction
, qui vous permet de choisir si vous souhaitez continuer avec la demande d'URL.
Certaines astuces Webview connues incluent le chargement de schémas d'URL personnalisés que le développeur n'a pas conçus, tels qu'un app-id:
pour lancer une application totalement différente ou SMS:
envoyer un texte. Notez que les vues Web incorporées n'affichent pas de barre avec l'adresse URL ou le statut SSL (l'icône de verrou). Par conséquent, l'utilisateur n'est pas en mesure de déterminer si la connexion est sécurisée..
Si la vue Web est en plein écran, par exemple, l'URL peut être piratée avec une page Web qui ressemble à votre écran de connexion, à l'exception du fait de diriger les informations d'identification vers un domaine malveillant. D'autres attaques dans le passé ont inclus des attaques de script entre sites qui ont provoqué la fuite de cookies et même du système de fichiers entier..
La meilleure prévention contre toutes les attaques mentionnées consiste à prendre le temps de concevoir votre interface à l'aide de contrôles d'interface utilisateur natifs au lieu d'afficher simplement une version Web dans votre application..
Jusqu'à présent, nous avons examiné des types d'attaques relativement simples. Mais terminons avec une attaque plus avancée qui peut arriver dans le runtime.
Tout comme Swift devient plus vulnérable lorsque vous vous connectez avec C, l'interface avec Objective-C apporte des vulnérabilités distinctes à la table.
Nous avons déjà vu les problèmes avec NSString
et les attaques par formatage. Un autre point est qu’Objective-C est beaucoup plus dynamique en tant que langage, permettant la transmission de types et de méthodes lâches. Si votre classe Swift hérite de NSObject
, alors il devient ouvert aux attaques d'exécution Objective-C.
La vulnérabilité la plus courante consiste à permuter de manière dynamique une méthode de sécurité importante pour une autre méthode. Par exemple, une méthode qui retourne si un utilisateur est validé peut être remplacée par une autre méthode qui retournera presque toujours true, telle que isRetinaDisplay
. Réduire au minimum l'utilisation d'Objective-C rendra votre application plus robuste contre ce type d'attaque.
Dans Swift 4, les méthodes sur les classes héritées d'une classe Objective-C ne sont exposées à l'exécution Objective-C que si ces méthodes ou les classes elles-mêmes sont marquées avec @attribut
. Souvent, la fonction Swift est appelée à la place, même si le @objc
attribut est utilisé. Cela peut arriver lorsque la méthode a un @objc
attribut mais n'est jamais réellement appelé depuis Objective-C.
En d'autres termes, Swift 4 introduit moins @objc
inférence, donc cela limite la surface d’attaque par rapport aux versions précédentes. Néanmoins, pour prendre en charge les fonctionnalités d'exécution, les fichiers binaires basés sur Objective-C doivent conserver de nombreuses informations de classe qui ne peuvent pas être supprimées. Ceci est suffisant pour que les ingénieurs inversés reconstruisent l'interface de classe afin de déterminer les sections de sécurité à corriger, par exemple.
Dans Swift, il y a moins d'informations exposées dans le binaire et les noms de fonction sont mutilés. Cependant, le changement peut être annulé par l'outil Xcode swift-demangle. En fait, les fonctions Swift ont un schéma de nommage cohérent, indiquant si chacune d’elles est une fonction Swift, une partie de classe, le nom et la longueur du module, le nom et la longueur de la classe, le nom et la longueur de la méthode, les attributs, les paramètres et le type de retour..
Ces noms sont plus courts dans Swift 4. Si vous êtes préoccupé par le reverse engineering, assurez-vous que la version publiée de votre application supprime les symboles en allant à Paramètres de construction> Déploiement> Supprimer les symboles rapides et en définissant l'option sur Oui.
Au-delà de l’obscurcissement du code de sécurité critique, vous pouvez également demander qu’il soit en ligne. Cela signifie que n'importe quel endroit où la fonction est appelée dans votre code, le code sera répété à cet endroit au lieu d'exister uniquement dans un emplacement du fichier binaire..
Ainsi, si un attaquant réussit à contourner un contrôle de sécurité particulier, cela n'affectera pas les autres occurrences de ce contrôle situées à d'autres endroits de votre code. Chaque vérification doit être corrigée ou accrochée, ce qui rend beaucoup plus difficile la réalisation d'une fissure. Vous pouvez utiliser un code en ligne comme ceci:
@inline (__ always) func myFunction () //…
Penser à la sécurité devrait faire partie intégrante du développement. Attendre simplement que la langue soit sécurisée peut conduire à des vulnérabilités qui auraient pu être évitées. Swift est populaire pour le développement iOS, mais il est disponible pour les applications de bureau macOS, tvOS, watchOS et Linux (vous pouvez donc l'utiliser pour les composants côté serveur où le potentiel d'exploits d'exécution de code est beaucoup plus élevé). Le sandboxing des applications peut être interrompu, comme dans le cas de périphériques jailbreakés autorisant l'exécution de code non signé. Il est donc important de penser à la sécurité et de faire attention aux notifications Xcode lors du débogage..
Un dernier conseil est de traiter les avertissements du compilateur comme des erreurs. Vous pouvez forcer Xcode à le faire en allant à Paramètres de construction et mise Traiter les avertissements comme des erreurs à Oui. N'oubliez pas de moderniser les paramètres de votre projet lors de la migration vers Xcode 9 pour obtenir des avertissements améliorés et, enfin et surtout, utiliser les nouvelles fonctionnalités disponibles en adoptant Swift 4 dès aujourd'hui.!
Nous avons créé un guide complet pour vous aider à apprendre Swift, que vous commenciez juste à vous familiariser avec les bases ou que vous souhaitiez explorer des sujets plus avancés..
Pour un aperçu des autres aspects du codage sécurisé pour iOS, consultez certains de mes autres articles ici sur Envato Tuts.+!