Von der Minimierung des Zeigergebrauchs bis zur starken Typüberprüfung zur Kompilierzeit ist Swift eine hervorragende Sprache für die sichere Entwicklung. Das bedeutet jedoch, dass es verlockend ist, die Sicherheit insgesamt zu vergessen. Es gibt immer noch Sicherheitslücken, und Swift lockt auch neue Entwickler an, die sich noch nicht mit Sicherheit vertraut gemacht haben.
Dieses Tutorial ist eine sichere Kodierungsanleitung, die sich mit Änderungen in Swift 4 sowie mit den neuen in Xcode 9 verfügbaren Werkzeugoptionen befasst, mit denen Sie Sicherheitslücken abschwächen können.
Viele Sicherheitslücken betreffen C und die Verwendung von Zeigern. Dies liegt daran, dass Sie mit Zeigern auf unbeschränkte Speicherorte zugreifen können, was das Lesen und Schreiben in den falschen Bereich erleichtert. Für Angreifer war es eine wichtige Möglichkeit, ein Programm in böswilliger Absicht zu ändern.
Swift verzichtet meistens auf Zeiger, ermöglicht aber trotzdem die Verbindung mit C. Viele APIs, einschließlich der gesamten Core Foundation API von Apple, basieren vollständig auf C, daher ist es sehr einfach, die Verwendung von Zeigern wieder in Swift einzuführen.
Zum Glück hat Apple die Zeigertypen entsprechend benannt: UnsafePointer
, UnsafeRawPointer
, UnsafeBufferPointer
, und UnsafeRawBufferPointer
. Es wird eine Zeit kommen, zu der die API, mit der Sie eine Schnittstelle herstellen, diese Typen zurückgibt, und die Hauptregel bei deren Verwendung ist Bewahren Sie Zeiger nicht zur späteren Verwendung auf. Zum Beispiel:
let myString = "Hallo Welt!" var unsafePointer: UnsafePointer? = nil myString.withCString myStringPointer in unsafePointer = myStringPointer // Irgendwann später… print (unsafePointer? .pointee)
Da wir auf den Zeiger außerhalb der Schließung zugegriffen haben, wissen wir nicht genau, ob der Zeiger noch auf den erwarteten Speicherinhalt zeigt. Der sichere Weg, den Zeiger in diesem Beispiel zu verwenden, besteht darin, ihn zusammen mit der print-Anweisung innerhalb des Abschlusses zu halten.
Zeiger auf Strings und Arrays haben ebenfalls keine Begrenzung. Dies bedeutet, dass es leicht ist, einen unsicheren Zeiger für ein Array zu verwenden, aber aus Versehen außerhalb der Begrenzung auf einen Pufferüberlauf zuzugreifen.
var numbers = [1, 2, 3, 4, 5] numbers.withUnsafeMutableBufferPointer Puffer im // ok Puffer [0] = 5 print (Puffer [0]) // fehlerhafter Puffer [5] = 0 print (Puffer [5 ])
Die gute Nachricht ist, dass Swift 4 versucht, die App zum Absturz zu bringen, anstatt mit dem fortzufahren, was aufgerufen werden würde undefiniertes Verhalten. Wir wissen nicht was Puffer [5]
verweist auf! Swift wird jedoch nicht jeden Fall erfassen. Setzen Sie nach dem folgenden Code einen Haltepunkt und sehen Sie sich die Variablen an ein
und c
. Sie werden auf gesetzt 999
.
func getAddress (Zeiger: UnsafeMutablePointer) -> UnsafeMutablePointer Rückkehrzeiger var a = 111 var b = 222 var c = 333 let zeiger: UnsafeMutablePointer = getAddress (pointer: & b) pointer.successor (). initialize (bis: 999) pointer.predecessor (). initialize (bis: 999)
Dies zeigt eine Paketüberfluss denn ohne explizite Zuordnung werden Variablen im Allgemeinen im Stack gespeichert.
Im nächsten Beispiel machen wir eine Zuordnung mit einer Kapazität von nur einer Int8
. Zuordnungen werden auf dem Heap gespeichert, sodass die nächste Zeile den Heap überläuft. In diesem Beispiel warnt Xcode Sie nur mit einem Hinweis in der Konsole bekommt
ist unsicher.
let buffer = UnsafeMutablePointer.zuweisen (Kapazität: 1) erhält (Puffer)
Was ist der beste Weg, um Überläufe zu vermeiden? Bei der Schnittstelle zu C ist es äußerst wichtig, die Eingabe von Grenzen zu überprüfen, um sicherzustellen, dass sie sich innerhalb der Reichweite befindet.
Sie denken vielleicht, dass es ziemlich schwierig ist, sich an alle verschiedenen Fälle zu erinnern. Um Ihnen dabei zu helfen, bietet Xcode ein sehr nützliches Tool namens Address Sanitizer.
Address Sanitizer wurde in Xcode 9 verbessert. Es ist ein Tool, mit dem Sie ungültigen Speicherzugriff abfangen können, wie in den Beispielen, die wir gerade gesehen haben. Wenn Sie mit dem arbeiten werden Unsicher*
Typen ist es eine gute Idee, das Address Sanitizer-Tool zu verwenden. Sie ist standardmäßig nicht aktiviert. Um sie zu aktivieren, gehen Sie zu Produkt> Schema> Bearbeitungsschema> Diagnose, und prüfe Adressdesinfektionsmittel. In Xcode 9 gibt es eine neue Unteroption, Erkennen Sie die Verwendung des Stapels nach der Rückkehr. Diese neue Option erkennt die Schwachstellen des Use-after-scope und des Use-after-return aus unserem ersten Beispiel.
Manchmal wird das übersehen Ganzzahlüberlauf. Dies liegt daran, dass Integer-Überläufe nur dann Sicherheitslücken darstellen, wenn sie als Index oder Größe eines Puffers verwendet werden oder wenn der unerwartete Wert des Überlaufs den Fluss des kritischen Sicherheitscodes ändert. Swift 4 fängt die offensichtlichsten Integer-Überläufe zur Kompilierzeit ab, z. B. wenn die Anzahl deutlich größer ist als der Maximalwert der Integer-Zahl.
Zum Beispiel wird Folgendes nicht kompiliert.
var someInteger: CInt = 2147483647 someInteger + = 1
Meistens wird die Nummer jedoch zur Laufzeit dynamisch eintreffen, beispielsweise wenn ein Benutzer Informationen in a eingibt UITextField
. Undefined Behavior Sanitizer ist ein neues Tool in Xcode 9, das vorzeichenbehaftete Integer-Überläufe und andere Typenkonflikte entdeckt. Um es zu aktivieren, gehen Sie zu Produkt> Schema> Bearbeitungsschema> Diagnose, und einschalten Undefined Behavior Sanitizer. Dann in Build-Einstellungen> Undefined Behavior Sanitizer, einstellen Aktivieren Sie zusätzliche Integer-Prüfungen zu Ja.
Es gibt noch ein anderes erwähnenswertes Verhalten über undefiniertes Verhalten. Obwohl pure Swift Zeiger, Referenzen und Kopien von Puffern immer noch hinter den Kulissen verbirgt, ist es möglich, auf ein Verhalten zu stoßen, mit dem Sie nicht gerechnet haben. Wenn Sie beispielsweise anfangen, Erfassungsindizes zu durchlaufen, könnten die Indizes während der Iteration versehentlich von Ihnen geändert werden.
Var-Nummern = [1, 2, 3] für Zahlen in Zahlen Drucknummern (Numbers) = [4, 5, 6] //<- accident ??? for number in numbers print(number)
Hier haben wir gerade das verursacht Zahlen
Array, um auf ein neues Array innerhalb der Schleife zu zeigen. Was macht dann? Nummer
zeigen auf? Normalerweise wird dies als "baumelnde Referenz" bezeichnet. In diesem Fall erstellt Swift jedoch implizit eine Referenz auf eine Kopie des Puffers Ihres Arrays für die Dauer der Schleife. Das bedeutet, dass die Anweisung print tatsächlich 1, 2 und 3 anstelle von 1, 4, 5 ausgibt. Das ist gut! Mit Swift sparen Sie vor undefiniertem Verhalten oder einem App-Absturz, auch wenn Sie diese Ausgabe möglicherweise nicht erwartet haben. Ihre Peer-Entwickler erwarten nicht, dass Ihre Sammlung während der Aufzählung mutiert wird. Achten Sie daher im Allgemeinen besonders darauf, dass Sie die Auflistung nicht ändern.
Daher hat Swift 4 zum Zeitpunkt des Kompilierens große Sicherheitsmaßnahmen, um diese Sicherheitslücken zu beheben. Es gibt viele Situationen, in denen die Sicherheitsanfälligkeit erst zur Laufzeit besteht, wenn Benutzerinteraktionen ausgeführt werden. Zu Swift gehört auch die dynamische Prüfung, durch die viele Probleme auch zur Laufzeit erkannt werden können. Es ist jedoch zu teuer für Threads, sodass sie nicht für Multithread-Code ausgeführt wird. Durch die dynamische Prüfung werden viele, aber nicht alle Verstöße gefangen. Daher ist es immer noch wichtig, sicheren Code zu schreiben!
Wenden wir uns nun einem anderen Bereich zu, in dem Angriffe durch Schwachstellen-Code-Injektionen durchgeführt werden.
Formatstring-Angriffe treten auf, wenn eine Eingabezeichenfolge in Ihrer App als Befehl analysiert wird, den Sie nicht beabsichtigten. Während reine Swift-Strings nicht anfällig für Format-String-Angriffe sind, kann der Objective-C NSString
und Core Foundation CFString
Klassen sind und sind von Swift erhältlich. Beide Klassen verfügen über Methoden wie stringWithFormat
.
Angenommen, der Benutzer kann einen beliebigen Text aus einem eingeben UITextField
.
let inputString = "Zeichenfolge aus einem Textfeld% @% d% p% ld% @% @" als NSString
Dies kann eine Sicherheitslücke sein, wenn die Formatzeichenfolge direkt behandelt wird.
let textFieldString = NSString.init (Format: inputString) // schlecht let textFieldString = NSString.init (Format: "% @", inputString) // gut
Swift 4 versucht, fehlende Argumente für das Formatstring durch die Rückgabe von 0 oder NULL zu behandeln. Es ist jedoch besonders problematisch, ob der String an die Objective-C-Laufzeitumgebung zurückgegeben wird.
NSLog (textFieldString); // bad NSLog ("% @", textFieldString); //gut
Während der falsche Weg meistens nur zum Absturz führt, kann ein Angreifer sorgfältig eine Formatzeichenfolge erstellen, um Daten in bestimmte Speicherbereiche im Stack zu schreiben, um das Verhalten Ihrer App zu ändern (z. B. das Ändern einer isAuthenticated
Variable).
Ein weiterer großer Täter ist NSPredicate
, Diese kann eine Formatzeichenfolge akzeptieren, mit der angegeben wird, welche Daten von Core Data abgerufen werden. Klauseln wie MÖGEN
und Enthält
erlauben Platzhalter und sollten vermieden oder zumindest nur für Suchanfragen verwendet werden. Die Idee ist, die Aufzählung von Konten zu vermeiden, beispielsweise wenn der Angreifer "a *" als Kontonamen eingibt. Wenn Sie das ändern MÖGEN
Klausel zu ==
, Dies bedeutet, dass die Zeichenfolge buchstäblich mit "a *" übereinstimmen muss..
Andere häufige Angriffe treten auf, wenn die Eingabezeichenfolge mit einem Anführungszeichen vorzeitig beendet wird, sodass zusätzliche Befehle eingegeben werden können. Beispielsweise könnte ein Login durch Eingabe umgangen werden ') ODER 1 = 1 ODER (Passwort LIKE' *
in die UITextField
. Diese Zeile bedeutet "wo Kennwort wie etwas ist", wodurch die Authentifizierung vollständig umgangen wird. Die Lösung besteht darin, jeglichen Injektionsversuchen durch Hinzufügen eigener doppelter Anführungszeichen im Code zu entgehen. Auf diese Weise werden alle zusätzlichen Anführungszeichen des Benutzers als Teil betrachtet der Eingabezeichenfolge anstelle eines speziellen abschließenden Zeichens:
let Abfrage = NSPredicate.init (Format: "Passwort == \"% @ \ "", Name)
Eine weitere Möglichkeit zum Schutz vor diesen Angriffen besteht darin, einfach nach bestimmten Zeichen zu suchen und diese auszuschließen, von denen Sie wissen, dass sie in der Zeichenfolge schädlich sein können. Beispiele wären Anführungszeichen oder sogar Punkte und Schrägstriche. Zum Beispiel ist es möglich, eine Verzeichnis-Traversal-Angriff wenn die Eingabe direkt an den übergeben wird Dateimanager
Klasse. In diesem Beispiel gibt der Benutzer "… /" ein, um das übergeordnete Verzeichnis des Pfads anstelle des beabsichtigten Unterverzeichnisses anzuzeigen.
let userControllerString = "... /" als NSString let sourcePath = NSString.init (Format: "% @ /% @", Bundle.main.resourcePath!, userControllerString) NSLog ("% @", sourcePath) // statt Build / Produkte / Debug / Swift4.app / Inhalt / Ressourcen, es wird Build / Products / Debug / Swift4.app / Inhalt sein lassen Filemanager: FileManager = FileManager () let files = filemanager.enumerator (atPath: sourcePath als String), während die Datei gelassen wird = Dateien? .nextObject () print (Datei)
Andere Sonderzeichen können ein NULL-Abschlussbyte enthalten, wenn der String als C-String verwendet wird. Zeiger auf C-Zeichenfolgen benötigen ein NULL-Abschlussbyte. Aus diesem Grund ist es möglich, die Zeichenfolge einfach durch Einfügen eines NULL-Bytes zu manipulieren. Der Angreifer möchte die Zeichenfolge möglicherweise vorzeitig beenden, wenn eine Flagge wie z needs_auth = 1
, oder wenn der Zugriff standardmäßig aktiviert und explizit deaktiviert ist, z. B. mit is_subscriber = 0
.
let userInputString = "Benutzername = Ralph \ 0" als NSString let commandString = NSString.init (Format: "subscribe_user:% @ & needs_authorization = 1", userInputString) NSLog ("% s", commandString.utf8String!) // druckt subscribe_user: username = Ralph statt subscribe_user: username = Ralph & needs_authorization = 1
Das Analysieren von HTML-, XML- und JSON-Zeichenfolgen erfordert ebenfalls besondere Aufmerksamkeit. Der sicherste Weg, um mit ihnen zu arbeiten, besteht darin, die systemeigenen Bibliotheken von Foundation zu verwenden, die Objekte für jeden Knoten bereitstellen, z NSXMLParser
Klasse. Swift 4 führt die typsichere Serialisierung in externe Formate wie JSON ein. Wenn Sie jedoch XML oder HTML mit einem benutzerdefinierten System lesen, müssen Sie sicherstellen, dass Sonderzeichen aus der Benutzereingabe nicht zur Anweisung des Interpreters verwendet werden können.
<
muss werden & lt
.>
sollte mit ersetzt werden & gt
.&
soll werden &Ampere
.“
oder '
müssen werden & quot
und & apos
, beziehungsweise.Hier ist ein Beispiel für eine schnelle Methode zum Entfernen oder Ersetzen bestimmter Zeichen:
var myString = "Zeichenfolge zum Bereinigen;" myString = myString.replacingOccurrences (of: ";", with: "")
Ein letzter Bereich für Injektionsangriffe sind URL-Handler. Stellen Sie sicher, dass Benutzereingaben nicht direkt in den benutzerdefinierten URL-Handlern verwendet werden Öffne URL
und didReceiveRemoteNotification
. Stellen Sie sicher, dass die URL Ihren Erwartungen entspricht, und dass der Benutzer nicht willkürlich Informationen eingeben kann, um Ihre Logik zu manipulieren. Lassen Sie den Benutzer beispielsweise nicht auswählen, auf welchem Bildschirm des Stapels nach Index navigiert werden soll, sondern nur bestimmte Bildschirme, die einen undurchsichtigen Bezeichner verwenden, z t = es84jg5urw
.
Wenn Sie verwenden WKWebView
In Ihrer App kann es sinnvoll sein, auch die URLs zu überprüfen, die dort geladen werden. Sie können überschreiben decisionPolicyFor navigationAction
, So können Sie wählen, ob Sie mit der URL-Anfrage fortfahren möchten.
Einige bekannte Webview-Tricks enthalten das Laden benutzerdefinierter URL-Schemas, die der Entwickler nicht vorhatte, wie z App-ID:
eine völlig andere App starten oder SMS:
einen Text senden Beachten Sie, dass eingebettete Webviews keine Leiste mit der URL-Adresse oder dem SSL-Status (das Sperrsymbol) anzeigen, sodass der Benutzer nicht feststellen kann, ob die Verbindung vertrauenswürdig ist.
Wenn die Webansicht beispielsweise im Vollbildmodus angezeigt wird, könnte die URL mit einer Webseite überfallen werden, die genau wie Ihr Anmeldebildschirm aussieht, außer dass die Anmeldeinformationen stattdessen an eine schädliche Domäne weitergeleitet werden. Andere Angriffe in der Vergangenheit waren Cross-Site-Scripting-Angriffe, bei denen Cookies und sogar das gesamte Dateisystem durchgesickert wurden.
Die beste Prävention für alle genannten Angriffe ist, dass Sie sich die Zeit nehmen, um Ihre Benutzeroberfläche mit nativen UI-Steuerelementen zu entwerfen, anstatt nur eine webbasierte Version in Ihrer App anzuzeigen.
Bisher haben wir relativ einfache Angriffe untersucht. Aber zum Abschluss noch einen fortgeschritteneren Angriff, der zur Laufzeit passieren kann.
So wie Swift anfälliger wird, wenn Sie mit C arbeiten, bringt die Verbindung mit Objective-C separate Schwachstellen in die Tabelle.
Wir haben die Probleme mit schon gesehen NSString
und formatieren Sie String-Attacken. Ein weiterer Punkt ist, dass Objective-C als Sprache viel dynamischer ist, sodass lose Typen und Methoden weitergegeben werden können. Wenn Ihre Swift-Klasse von erbt NSObject
, dann ist es offen für Objective-C-Laufzeitangriffe.
Die häufigste Sicherheitsanfälligkeit besteht darin, eine wichtige Sicherheitsmethode dynamisch gegen eine andere Methode auszutauschen. Beispielsweise könnte eine Methode, die zurückgegeben wird, wenn ein Benutzer validiert ist, gegen eine andere Methode ausgetauscht werden, die fast immer true zurückgibt, wie z isRetinaDisplay
. Durch die Minimierung der Verwendung von Objective-C wird Ihre App gegen diese Art von Angriff robuster.
In Swift 4 werden Methoden für Klassen, die von einer Objective-C-Klasse erben, nur für die Objective-C-Laufzeit verfügbar gemacht, wenn diese Methoden oder die Klassen selbst mit gekennzeichnet sind @Attribut
. Oft wird stattdessen die Swift-Funktion aufgerufen, auch wenn die @objc
Attribut wird verwendet. Dies kann passieren, wenn die Methode über eine @objc
Attribut, wird aber nie von Objective-C aufgerufen.
Mit anderen Worten führt Swift 4 weniger ein @objc
Inferenz, so begrenzt dies die Angriffsfläche im Vergleich zu früheren Versionen. Um die Laufzeitfunktionen zu unterstützen, müssen Objective-C-basierte Binärdateien jedoch viele Klasseninformationen enthalten, die nicht entfernt werden können. Dies ist für Reverse-Ingenieure ausreichend, um die Klassenschnittstelle neu zu erstellen, um beispielsweise herauszufinden, welche Sicherheitsabschnitte zu patchen sind.
In Swift werden weniger Informationen in der Binärdatei angezeigt, und Funktionsnamen werden beschädigt. Das Mangeln kann jedoch mit dem Xcode-Tool swift-demangle rückgängig gemacht werden. In der Tat haben Swift-Funktionen ein konsistentes Benennungsschema, das angibt, ob jede eine Swift-Funktion ist oder nicht, Teil einer Klasse, Modulname und -länge, Klassenname und -länge, Methodenname und -länge, Attribute, Parameter und Rückgabetyp.
Diese Namen sind in Swift 4 kürzer. Wenn Sie sich mit Reverse Engineering beschäftigen, stellen Sie sicher, dass die Release-Version Ihrer App Symbole entfernt, indem Sie auf gehen Build-Einstellungen> Bereitstellung> Strip Swift-Symbole und die Option auf einstellen Ja.
Neben dem Verschleiern kritischer Sicherheitscodes können Sie auch die Inline-Eingabe anfordern. Das bedeutet, dass an jeder Stelle, an der die Funktion in Ihrem Code aufgerufen wird, der Code an dieser Stelle wiederholt wird, anstatt nur an einer Stelle der Binärdatei zu existieren.
Wenn es einem Angreifer gelingt, eine bestimmte Sicherheitsüberprüfung zu umgehen, hat dies keine Auswirkungen auf andere Vorkommen dieser Überprüfung, die sich an anderen Stellen des Codes befinden. Jeder Check muss geflickt oder angehakt werden, was die erfolgreiche Durchführung eines Cracks erheblich erschwert. Sie können Inline-Code wie folgt eingeben:
@inline (__ immer) func myFunction () //…
Das Nachdenken über Sicherheit sollte ein großer Teil der Entwicklung sein. Wenn Sie nur davon ausgehen, dass die Sprache sicher ist, kann dies zu Schwachstellen führen, die hätte vermieden werden können. Swift ist für die iOS-Entwicklung beliebt, aber es ist für MacOS-Desktop-Apps, tvOS, watchOS und Linux verfügbar (Sie könnten es für serverseitige Komponenten verwenden, bei denen das Potenzial für die Ausführung von Code viel höher ist). Das Sandboxing von Apps kann unterbrochen werden, beispielsweise bei Geräten mit Jailbroken, bei denen nicht signierter Code ausgeführt werden kann. Daher ist es wichtig, beim Debuggen immer noch über Sicherheitsaspekte nachzudenken und auf Xcode-Hinweise zu achten.
Ein letzter Tipp ist, Compiler-Warnungen als Fehler zu behandeln. Sie können Xcode dazu zwingen, indem Sie zu gehen Einstellungen erstellen und Einstellung Warnungen als Fehler behandeln zu Ja. Vergessen Sie nicht, Ihre Projekteinstellungen bei der Migration auf Xcode 9 zu modernisieren, um verbesserte Warnungen zu erhalten, und nutzen Sie die neuen Funktionen von Swift 4!
Wir haben ein umfassendes Handbuch zusammengestellt, um Sie beim Erlernen von Swift zu unterstützen, egal ob Sie gerade mit den Grundlagen anfangen oder fortgeschrittenere Themen erkunden möchten.
Eine Einführung in andere Aspekte der sicheren Kodierung für iOS finden Sie hier in Envato Tuts+!