Säker kodning i Swift 4

Från att minimera pekaren använd till stark typkontroll vid kompileringstid, är Swift ett bra språk för säker utveckling. Men det betyder att det är frestande att glömma säkerheten helt och hållet. Det finns fortfarande sårbarheter, och Swift ljuger också till nya utvecklare som ännu inte har lärt sig om säkerhet. 

Denna handledning är en säker kodningsguide som kommer att adressera ändringar i Swift 4 samt de nya verktygsalternativen som finns i Xcode 9 som hjälper dig att mildra säkerhetsproblem.

Pekare och överflöden

Många säkerhetsproblem har vridit sig runt C och dess användning av pekare. Det beror på att pekare låter dig komma åt råminnesplatser, vilket gör det lättare att läsa och skriva till fel område. Det har varit ett viktigt sätt för angripare att skadligt ändra ett program. 

Swift gör det vanligtvis inte med pekare, men det låter dig fortfarande gränsa med C. Många API, inklusive Apples hela Core Foundation API, är helt baserade på C, så det är väldigt enkelt att introducera användningen av pekare tillbaka till Swift. 

Lyckligtvis har Apple namnet pekartyperna lämpligt: UnsafePointer, UnsafeRawPointerUnsafeBufferPointer, och UnsafeRawBufferPointer. Det kommer att komma en tid då API: n som du har gränssnitt till kommer att returnera dessa typer, och huvudregeln när du använder dem är Förvara inte eller returnera pekare för senare användning. Till exempel:

låt myString = "Hej världen!" var osäkerPointer: OsäkerPointer? = nil myString.withCString myStringPointer i unsafePointer = myStringPointer // någon gång senare ... print (unsafePointer? .pointee)

Eftersom vi nått pekaren utanför stängningen vet vi inte säkert om pekaren fortfarande pekar på det förväntade minnesinnehållet. Det säkra sättet att använda pekaren i det här exemplet skulle vara att hålla det, tillsammans med utskriftsförklaringen, inom stängningen. 

Pekare till strängar och arrays har också ingen gränskontroll. Det betyder att det är lätt att använda en osäker pekare på en matrice men av misstag komma åt dess gräns - ett buffertflöde.

var nummer = [1, 2, 3, 4, 5] siffror. medUnsafeMutableBufferPointer buffert i // ok buffert [0] = 5 skriv ut (buffert [0]) // dålig buffert [5] = 0 utskrift ])

Den goda nyheten är att Swift 4 försöker krascha appen istället för att fortsätta med vad som skulle kallas odefinierat beteende. Vi vet inte vad buffert [5] pekar på! Swift kommer dock inte att fånga alla fall. Ange en brytpunkt efter följande kod och titta på variabler en och c. De kommer att ställas in på 999.

func getAddress (pekare: UnsafeMutablePointer) -> UnsafeMutablePointer returpekare var a = 111 var b = 222 var c = 333 låt pekaren: UnsafeMutablePointer = getAddress (pekare: & b) pointer.successor (). initiera (till: 999) pointer.predecessor (). initiera (till: 999)

Detta visar a stack överflöde eftersom utan en explicit tilldelning lagras variabler generellt på stapeln. 

I nästa exempel gör vi en fördelning med en kapacitet på endast en singel int8. Tilldelningar lagras på högen, så nästa linje kommer att överfälla högen. I det här exemplet varnar Xcode dig endast med en anteckning i konsolen som blir är osäker.

låt buffert = osäkerMutabelPointer.allokera (kapacitet: 1) får (buffert) 

Så vad är det bästa sättet att undvika överflöd? Det är oerhört viktigt att gränssnittet kontrollerar ingången för att se till att det ligger inom intervallet. 

Du kanske tror att det är ganska svårt att komma ihåg och hitta alla de olika fallen. Så för att hjälpa dig kommer Xcode med ett mycket användbart verktyg som heter Address Sanitizer. 

Adress Sanitizer har förbättrats i Xcode 9. Det är ett verktyg som hjälper dig att fånga ogiltig minnesåtkomst, till exempel de exempel vi just har sett. Om du kommer att arbeta med Osäker* typer, det är en bra idé att använda verktyget Address Sanitizer. Det är inte aktiverat som standard, så att aktivera det går till Produkt> Schema> Redigeringsschema> Diagnostik, och kolla Adress Sanitizer. I Xcode 9 finns ett nytt underalternativ, Upptäck användning av stapel efter retur. Det här nya alternativet upptäcker användbarhet efter användning och användbarhet efter avkastning i vårt första exempel.

Ibland förbises det heltals överflöde. Detta beror på att heltal överflöden endast är säkerhetshål när de används som ett index eller en bufferts storlek eller om det oväntade värdet av överflödet ändrar flödet av kritisk säkerhetskod. Swift 4 fångar mest uppenbara heltal överflöd vid sammanställningstid, till exempel när numret är klart större än det maximala värdet av heltalet. 

Till exempel kommer inte följande att kompilera.

var someInteger: CInt = 2147483647 someInteger + = 1

Men många gånger kommer numret att komma dynamiskt vid körning, till exempel när en användare anger information i en UITextField. Odefinierat beteende Sanitizer är ett nytt verktyg i Xcode 9 som detekterar signerat heltal överflöde och andra typmatchningsfel. För att aktivera det, gå till Produkt> Schema> Redigeringsschema> Diagnostik, och sätt på Odefinierad beteende Sanitizer. Sedan i Bygga inställningar> Odefinierad beteende Sanitizer, uppsättning Aktivera extra integerkontroller till Ja.

Det finns en annan sak som är värt att nämna om odefinierat beteende. Trots att rena Swift hide pointers, referenser och kopior av buffertar fortfarande används bakom kulisserna, så det är möjligt att springa in i beteende som du inte hade förväntat dig. När du till exempel börjar iterera över insamlingsindex kan indexerna av misstag ändras av dig under iteration.

var tal = [1, 2, 3] för antal i siffror skriv ut (nummer) nummer = [4, 5, 6] //<- accident ???  for number in numbers  print(number) 

Här orsakade vi bara tal array för att peka på en ny array i slingan. Vad gör då siffra peka mot? Det här brukar kallas en dangling-referens, men i det här fallet skapar Swift implicit en referens till en kopia av bufferten i din array under loopens längd. Det betyder att utskriftsdeklarationen faktiskt kommer att skrivas ut 1, 2 och 3 istället för 1, 4, 5 ... Det här är bra! Swift sparar dig från odefinierat beteende eller en appkrasch, även om du kanske inte har förväntat dig att det heller är output. Din peer-utvecklare förväntar dig inte att din samling ska muteras under uppräkning, så allmänt, var extra försiktig vid uppräkning att du inte ändrar samlingen.

Så Swift 4 har stor säkerhetshantering vid kompileringstid för att fånga dessa säkerhetsproblem. Det finns många situationer där sårbarheten inte existerar förrän körtid när det finns användarinteraktion. Swift inkluderar också dynamisk kontroll, som kan fånga många av problemen vid körning också, men det är för dyrt att göra över trådar så att det inte utförs för multithreaded-kod. Dynamisk kontroll kommer att fånga många men inte alla överträdelser, så det är fortfarande viktigt att skriva säker kod i första hand! 

Med det, låt oss vända oss till ett annat mycket vanligt område för sårbarheter-kodinjektionsattacker.

Injicera och formatera strängattacker

Formatera strängattacker inträffar när en inmatningssträng analyseras i din app som ett kommando som du inte hade för avsikt att göra. Medan rena Swift-strängar inte är mottagliga för att formatera strängattacker, mål-C NSString och Core Foundation CFString klasser är och de är tillgängliga från Swift. Båda dessa klasser har metoder som stringWithFormat.

Låt oss säga att användaren kan skriva in godtycklig text från a UITextField.

låt inputString = "String från ett textfält% @% d% p% ld% @% @" som NSString

Det här kan vara ett säkerhetshål om formatsträngen hanteras direkt.

låt textFieldString = NSString.init (format: inputString) // dålig låt textFieldString = NSString.init (format: "% @", inputString) // bra

Swift 4 försöker hantera saknade formatsträngargument genom att returnera 0 eller NULL, men det är särskilt ett problem om strängen kommer att gå vidare till Objective-C runtime.

NSLog (textFieldString); // dålig NSLog ("% @", textFieldString); //Bra

Medan oftast det felaktiga sättet bara orsakar en krasch, kan en angripare noga skapa en formatsträng för att skriva data till specifika minnesplatser på stapeln för att ändra ditt appbeteende (till exempel byte av en isAuthenticated variabel). 

En annan stor synder är NSPredicate, som kan acceptera en formatsträng som används för att ange vilken data som hämtas från Core Data. Klausuler som TYCKA OM och INNEHÅLLER tillåta jokertecken och bör undvikas, eller åtminstone endast användas för sökningar. Tanken är att undvika uppräkning av konton, till exempel, där angriparen anger "a *" som kontonamn. Om du ändrar TYCKA OM klausul till ==, detta betyder att strängen bokstavligen måste matcha "a *". 

Andra vanliga attacker händer genom att avsluta inmatningssträngen tidigt med ett citatstecken så att ytterligare kommandon kan anges. Till exempel kan en inloggning kringgå genom att skriva in ') ELLER 1 = 1 ELLER (Lösenord LIKE' * in i UITextField. Den här linjen översätter till "var lösenord är som någonting", vilket kringgår autentiseringen helt. Lösningen är att helt undvika försök att injicera genom att lägga till egna dubbla citat i kod. På så sätt ses eventuella ytterligare citat från användaren som del av inmatningssträngen istället för att vara en särskild avslutande karaktär:

låt fråga = NSPredicate.init (format: "lösenord == \"% @ \ "", namn)

Ett annat sätt att skydda mot dessa attacker är att helt enkelt söka efter och utesluta specifika tecken som du vet kan vara skadliga i strängen. Exempel skulle innehålla citat, eller till och med prickar och snedstreck. Det är till exempel möjligt att göra a katalog traversal attack när inmatning skickas direkt till Filhanterare klass. I det här exemplet anger användaren "... /" för att visa huvudkatalogen för sökvägen i stället för den avsedda underkatalogen.

låt userControllerString = "... /" som NSString låt sourcePath = NSString.init (format: "% @ /% @", Bundle.main.resourcePath!, userControllerString) NSLog ("% @", sourcePath) // Istället för att bygga / Produkter / Debug / Swift4.app / Innehåll / Resources, det kommer att bli Build / Products / Debug / Swift4.app / Innehåll låter filhanteraren: FileManager = FileManager () släpp filer = filemanager.enumerator (atPath: sourcePath as String) = filer? .nextObject () print (file)

Andra specialtecken kan innehålla en NULL avslutande byte om strängen används som en C-sträng. Pekare till C-strängar kräver en NULL avslutande byte. På grund av detta är det möjligt att manipulera strängen helt enkelt genom att införa en NULL-byte. Attackeren kanske vill avsluta strängen tidigt om det fanns en flagga som needs_auth = 1, eller när åtkomst är aktiverad som standard och avaktiveras exakt som med is_subscriber = 0.

låt userInputString = "användarnamn = Ralph \ 0" som NSString låt commandString = NSString.init (format: "subscribe_user:% @ & needs_authorization = 1", userInputString) NSLog ("% s", commandString.utf8String!) // prints subscribe_user: användarnamn = Ralph istället för subscribe_user: användarnamn = Ralph & needs_authorization = 1

Att analysera HTML, XML och JSON-strängar kräver särskild uppmärksamhet. Det säkraste sättet att arbeta med dem är att använda Foundations inhemska bibliotek som tillhandahåller objekt för varje nod, till exempel NSXMLParser klass. Swift 4 introducerar typsäker serialisering till externa format som JSON. Men om du läser XML eller HTML med ett anpassat system, se till att specialtecken från användarinmatningen inte kan användas för att instruera tolken.

  • < måste bli & lt.
  • > bör ersättas med & gt.
  • & bör bli & amp.
  • Innehållsattributvärden, vilken som helst eller ' måste bli & quot och N', respektive.

Här är ett exempel på ett snabbt sätt att ta bort eller ersätta specifika tecken:

var myString = "sträng för att desinficera;" myString = myString.replacingOccurrences (av: ";", med: "")

Ett sista område för injektionsattacker är inuti URL-hanterare. Kontrollera att användarinmatning inte används direkt inuti de anpassade URL-hanterarna openURL och didReceiveRemoteNotification. Verifiera att webbadressen är vad du förväntar dig och att det inte tillåter en användare att medvetet skriva in information för att manipulera din logik. Till exempel, i stället för att låta användaren välja vilken skärm i stapeln som ska navigera till med index, tillåta endast specifika skärmar med en ogenomskinlig identifierare, t.ex. t = es84jg5urw

Om du använder WKWebViews i din app kan det vara bra att kolla de webbadresser som kommer att laddas där också. Du kan åsidosätta decidePolicyFor navigationAction, vilket låter dig välja om du vill fortsätta med webbadressförfrågan. 

Vissa kända webview-tricks inkluderar laddning anpassade webbadresser som utvecklaren inte hade för avsikt att göra, till exempel en app-id: att starta en helt annan app eller SMS: att skicka en text Observera att inbäddade webbvyer inte visar en streck med URL-adressen eller SSL-statusen (låsikonen), så att användaren inte kan bestämma om anslutningen är betrodd. 

Om webbvyn är fullskärm, kan URL: n exempelvis kapas med en webbsida som ser ut som din inloggningsskärm, förutom att rikta in referenserna till en skadlig domän istället. Andra attacker i det förflutna har inkluderat skriptattacker på platsen som har läckt kakor och till och med hela filsystemet. 

Det bästa förebyggandet av alla nämnda attacker är att ta tid att designa ditt gränssnitt med inbyggda gränssnittskontroller istället för att bara visa en webbaserad version i din app.

Hittills har vi tittat på relativt enkla typer av attacker. Men låt oss avsluta med en mer avancerad attack som kan hända under körtiden.

Runtime Hacking

Precis som Swift blir mer sårbart när du gränsar till C, kommer gränssnitt med Objective-C att medföra separata sårbarheter mot bordet. 

Vi har redan sett problemen med NSString och formatera strängattacker. En annan sak är att mål-C är mycket mer dynamisk som ett språk, så att lösa typer och metoder kan överföras. Om din Swift-klass ärver från NSObject, då blir det öppet för Objective-C runtime attacker. 

Den vanligaste sårbarheten innebär att du byter en viktig säkerhetsmetod för en annan metod. Exempelvis kan en metod som returnerar om en användare valideras bytas mot en annan metod som nästan alltid kommer att returneras sant, till exempel isRetinaDisplay. Minimera användningen av mål-C kommer att göra din app mer robust mot denna typ av attack.

I Swift 4 exponeras metoder för klasser som ärver från en mål-C-klass endast för Objective-C runtime om dessa metoder eller klasserna själva är markerade med @attribut. Ofta kallas Swift-funktionen istället, även om @objc attributet används. Detta kan hända när metoden har en @objc attribut men kallas aldrig faktiskt från mål-C. 

Med andra ord introducerar Swift 4 mindre @objc inferens, så detta begränsar attackytan jämfört med tidigare versioner. För att stödja runtime-funktionerna behöver objektiv-C-baserade binarier behålla en hel del klassinformation som inte kan avlägsnas. Det här räcker för omvänd ingenjörer att återuppbygga klassgränssnittet för att ta reda på vilka säkerhetsavsnitt som ska patchas, till exempel. 

I Swift finns mindre information exponerad i binären, och funktionsnamn är manglade. Mangling kan dock ångras av Xcode-verktyget swift-demangle. Faktum är att Swift-funktioner har en konsekvent namngivning, vilket indikerar om var och en är en Swift-funktion eller inte, en del av en klass, modulnamn och längd, klassnamn och längd, metodnamn och längd, attribut, parametrar och returtyp. 

Dessa namn är kortare i Swift 4. Om du är oroad över omvänd teknik, se till att versionsversionen av dina appremsor symboler går genom att gå till Bygg inställningar> Distribution> Strip Swift Symbols och ställer in alternativet till Ja.

Utöver obfuscating kritisk säkerhetskod kan du också begära att den är inline. Det betyder att varje plats som funktionen heter i din kod, kommer koden att upprepas på den platsen istället för att existera endast på en plats i binären. 

På så sätt påverkar inte en angripare en viss säkerhetskontroll någon annan händelse i den kontrollen som ligger på andra ställen i din kod. Varje kontroll måste patchas eller anslutas, vilket gör det mycket svårare att framgångsrikt utföra en spricka. Du kan inline kod så här:

@inline (__ alltid) func myFunction () // ...

Slutsats

Att tänka på säkerhet bör vara en stor del av utvecklingen. Att bara förvänta sig att språket är säkert kan leda till sårbarheter som kunde ha undvikits. Swift är populärt för iOS-utveckling, men det är tillgängligt för MacOS desktop apps, tvOS, watchOS och Linux (så du kan använda den för komponenter på serverns sida där potentialen för kodkörning utnyttjas är mycket högre). App sandboxning kan brytas, till exempel när det gäller jailbroken enheter som tillåter att oskickad kod körs, så det är viktigt att du fortfarande tänker på säkerhet och var uppmärksam på Xcode-meddelanden medan du debuggar. 

Ett sista tips är att behandla kompilatorvarningar som fel. Du kan tvinga Xcode att göra detta genom att gå till Bygg inställningar och inställning Behandla varningar som fel till Ja. Glöm inte att modernisera dina projektinställningar när migrering till Xcode 9 för att få förbättrade varningar, och sist men inte minst, utnyttja de nya funktionerna som finns tillgängliga genom att anta Swift 4 idag!

Lär dig Swift

Vi har byggt en komplett guide för att hjälpa dig att lära dig Swift, oavsett om du bara har börjat med grunderna eller vill utforska mer avancerade ämnen.

För en primer på andra aspekter av säker kodning för iOS, kolla in några av mina andra inlägg här på Envato Tuts+!