Sikker koding i Swift 4

Fra å minimere pekeren bruker du sterk kontroll på kompileringstid, er Swift et godt språk for sikker utvikling. Men det betyr at det er fristende å glemme sikkerheten helt. Det er fortsatt sårbarheter, og Swift lurer også på nye utviklere som ennå ikke har lært om sikkerhet. 

Denne veiledningen er en sikker kodingsguide som adresserer endringer i Swift 4, samt de nye verktøyalternativene som er tilgjengelige i Xcode 9, som vil hjelpe deg med å redusere sikkerhetsproblemer.

Pekere og overflyt

Mange sikkerhetsproblemer har rotert rundt C og bruk av pekere. Dette skyldes at pekere gir deg tilgang til råminneslokaler, noe som gjør det lettere å lese og skrive til feil område. Det har vært en viktig måte for angripere å skadelig endre et program. 

Swift gjør det meste bort med pekere, men det lar deg fortsatt grensesnittet med C. Mange APIer, inkludert Apples hele Core Foundation API, er helt basert på C, så det er veldig enkelt å introdusere bruk av pekere tilbake til Swift. 

Heldigvis har Apple navngitt peker typene på riktig måte: UnsafePointer, UnsafeRawPointerUnsafeBufferPointer, og UnsafeRawBufferPointer. Det kommer en tid da API-en du har kontakt med, vil returnere disse typene, og hovedregelen når du bruker dem er Ikke lagre eller returnere pekere til senere bruk. For eksempel:

la myString = "Hei verden!" var usikkerPointer: UsikkerPointer? = nil myString.withCString myStringPointer i unsafePointer = myStringPointer // en gang senere ... print (unsafePointer? .pointee)

Fordi vi åpnet pekeren utenfor lukkingen, vet vi ikke sikkert om pekeren fortsatt peker på forventet minneinnhold. Den sikre måten å bruke pekeren på i dette eksemplet, ville være å beholde den, sammen med utskriftserklæringen, innenfor lukningen. 

Pekere til strenge og arrays har også ingen grensekontroll. Dette betyr at det er enkelt å bruke en usikker peker på en matrise, men ved et uhell få tilgang utover grensen - en bufferoverløp.

var tall = [1, 2, 3, 4, 5] tall. medUnsafeMutableBufferPointer buffer i // ok buffer [0] = 5 utskrift (buffer [0]) // dårlig buffer [5] = 0 utskrift ])

Den gode nyheten er at Swift 4 forsøker å krasje appen i stedet for å fortsette med det som ville bli kalt udefinert oppførsel. Vi vet ikke hva buffer [5] poeng til! Imidlertid vil Swift ikke fange hvert tilfelle. Angi et brytepunkt etter følgende kode og se på variabler en og c. De vil bli satt til 999.

func getAddress (pointer: UnsafeMutablePointer) -> UsikkerMutabelPointer returpeker var a = 111 var b = 222 var c = 333 la pekeren: UsikkerMutabelPointer = getAddress (pointer: & b) pointer.successor (). initialiser (til: 999) pointer.predecessor (). initialiser (til: 999)

Dette viser a Stabel overflow fordi uten en eksplisitt tildeling lagres variabler generelt på stabelen. 

I det neste eksemplet lager vi en allokering med en kapasitet på bare en enkelt Int8. Allokeringer lagres i bunken, så neste linje vil overfylle bunken. For dette eksempelet advarer Xcode deg kun med et notat i konsollen som blir er usikre.

la buffer = UsikkerMutabelPointer.allokere (kapasitet: 1) får (buffer) 

Så hva er den beste måten å unngå overløp? Det er ekstremt viktig når du kobler til C for å gjøre grenser sjekker på inngangen for å sikre at den er innenfor rekkevidde. 

Du kan tenke at det er ganske vanskelig å huske og finne alle de forskjellige sakene. Så for å hjelpe deg, kommer Xcode med et veldig nyttig verktøy som heter Address Sanitizer. 

Adresse Sanitizer er forbedret i Xcode 9. Det er et verktøy som hjelper deg med å få ugyldig minnetilgang, for eksempel eksemplene vi nettopp har sett. Hvis du vil jobbe med usikre * typer, er det en god ide å bruke verktøyet Address Sanitizer. Det er ikke aktivert som standard, så å aktivere det, gå til Produkt> Ordning> Rediger skjema> Diagnostikk, og sjekk Adresse Sanitizer. I Xcode 9 er det et nytt underalternativ, Oppdag bruk av stabel etter retur. Dette nye alternativet oppdager sårbarhet etter bruk og bruk etter retur fra vårt første eksempel.

Noen ganger overses er heltall overløp. Dette skyldes at heltall overløb bare er sikkerhetshull når de brukes som en indeks eller størrelse på en buffer, eller hvis den uventede verdien av overløpet endrer strømmen av kritisk sikkerhetskode. Swift 4 fanger mest åpenbare integeroverløp ved kompileringstid, for eksempel når tallet er klart større enn den maksimale verdien av heltalet. 

For eksempel vil ikke følgende kompilere.

var noenInteger: CInt = 2147483647 someInteger + = 1

Men mange ganger vil nummeret komme dynamisk på kjøretid, for eksempel når en bruker skriver inn informasjon i en UITextField. Udefinert adferd Sanitizer er et nytt verktøy i Xcode 9 som oppdager signert integeroverløp og andre type mismatch-feil. For å aktivere det, gå til Produkt> Ordning> Rediger skjema> Diagnostikk, og slå på Udefinert oppførsel Sanitizer. Så i Bygg innstillinger> Udefinert oppførsel Sanitizer, sett Aktiver ekstra helhetskontroller til Ja.

Det er en annen ting verdt å nevne om udefinert oppførsel. Selv om rene Swift skjuler pekere, er referanser og kopier av buffere fortsatt brukt bak kulissene, så det er mulig å kjøre inn i atferd som du ikke hadde forventet. For eksempel, når du begynner å repetere over innsamlingsindeksene, kan indeksene ved et uhell bli endret av deg under iterasjon.

var tall = [1, 2, 3] for nummer i tall skriv ut (tall) tall = [4, 5, 6] //<- accident ???  for number in numbers  print(number) 

Her har vi nettopp forårsaket tall array for å peke til et nytt array inne i sløyfen. Så hva gjør Nummer peker på? Dette vil normalt bli kalt en dangling referanse, men i dette tilfellet oppretter Swift implisitt en referanse til en kopi av bufferen i din matrise i løpet av løkken. Det betyr at utskriftserklæringen faktisk skal skrive ut 1, 2 og 3 i stedet for 1, 4, 5 ... Dette er bra! Swift lagrer deg fra udefinert oppførsel eller et programkrasj, selv om du kanskje ikke har forventet at det også er output. Dine kollegerutviklere forventer ikke at samlingen din blir mutert under opptelling, så generelt, vær ekstra forsiktig under oppregningen at du ikke endrer samlingen.

Så Swift 4 har stor sikkerhetshåndhevelse på kompileringstid for å fange disse sikkerhetsproblemene. Det er mange situasjoner der sikkerhetsproblemet ikke eksisterer før driftstid når det er brukerinteraksjon. Swift inkluderer også dynamisk kontroll, som kan ta mange av problemene i kjøretid også, men det er for dyrt å gjøre på tvers av tråder, slik at det ikke utføres for multithreaded kode. Dynamisk kontroll vil få mange, men ikke alle brudd, så det er fortsatt viktig å skrive sikker kode i utgangspunktet! 

Med det, la oss vende oss til et annet svært vanlig område for sårbarheter-kodeinjeksjonsangrep.

Injeksjon og format strengangrep

Formatere strengangrep skjer når en inntastingsstreng blir analysert i appen din som en kommando du ikke hadde til hensikt å gjøre. Mens rene Swift-strenge ikke er mottakelige for å formatere strengangrep, er Objective-C NSString og Core Foundation CFString klasser er, og de er tilgjengelige fra swift. Begge disse klassene har metoder som stringWithFormat.

La oss si at brukeren kan skrive inn vilkårlig tekst fra a UITextField.

la inputString = "String fra en tekstfelt% @% d% p% ld% @% @" som NSString

Dette kan være et sikkerhetshull hvis formatstrengen håndteres direkte.

la textFieldString = NSString.init (format: inputString) // dårlig la textFieldString = NSString.init (format: "% @", inputString) // god

Swift 4 prøver å håndtere manglende format streng argumenter ved å returnere 0 eller NULL, men det er spesielt en bekymring hvis strengen vil bli sendt tilbake til Objective-C runtime.

NSLog (textFieldString); // dårlig NSLog ("% @", textFieldString); //flink

Mens det meste av tiden vil feil feil bare forårsake en krasj, kan en angriper nøye lage en formatstreng for å skrive data til bestemte minnesteder på stakken for å endre appadferd (for eksempel å endre en isAuthenticated variabel). 

En annen stor skyldige er NSPredicate, som kan akseptere en formatstreng som brukes til å spesifisere hvilke data som hentes fra Core Data. Klausuler som SOM og INNEHOLDER tillat jokertegn og bør unngås, eller i det minste bare brukes til søk. Tanken er å unngå oppregning av kontoer, for eksempel hvor angriperen går inn i "a *" som kontonavn. Hvis du endrer SOM klausul til ==, dette betyr at strengen bokstavelig talt må samsvare med "a *". 

Andre vanlige angrep skjer ved å avslutte inntastingsstrengen tidlig med et enkeltattest, slik at flere kommandoer kan skrives inn. For eksempel kan en innlogging bli omgått ved å skrive inn ') ELLER 1 = 1 ELLER (passord LIKE' * inn i det UITextField. Denne linjen oversetter til "hvor passord er som noe", som omgår autentiseringen helt. Løsningen er å fullstendig unnslippe alle forsøk på injeksjon ved å legge til dine egne dobbelte anførselstegn i koden. På den måten blir eventuelle flere anførselstegn fra brukeren sett som en del av inntastingsstrengen i stedet for å være en spesiell avslutende karakter:

la spørringen = NSPredicate.init (format: "passord == \"% @ \ "" navn)

En annen måte å beskytte mot disse angrepene er å bare søke etter og utelukke bestemte tegn som du vet kan være skadelig i strengen. Eksempler vil inkludere sitater, eller til og med prikker og skråstreker. For eksempel er det mulig å gjøre en katalog traversal angrep når inngang blir sendt direkte til Filbehandler klasse. I dette eksemplet går brukeren inn i "... /" for å se hovedkatalogen på banen i stedet for den tilsiktede underkatalogen.

la userControllerString = "... /" som NSString la sourcePath = NSString.init (format: "% @ /% @", Bundle.main.resourcePath !, userControllerString) NSLog ("% @", sourcePath) // I stedet for å bygge / Produkter / Debug / Swift4.app / Innhold / Ressurser, det blir Bygg / Produkter / Debug / Swift4.app / Innhold La filemanager: FileManager = FileManager () la filer = filemanager.enumerator (atPath: sourcePath as String) mens la fil = filer? .nextObject () print (file)

Andre spesialtegn kan inneholde en NULL-avslutningsbyte hvis strengen blir brukt som en C-streng. Pekere til C-strengene krever en null avslutningsbyte. På grunn av dette er det mulig å manipulere strengen ved å introdusere en NULL byte. Angriperen vil kanskje si opp strengen tidlig hvis det var et flagg, for eksempel needs_auth = 1, eller når tilgang er på som standard og slått av eksplisitt som med is_subscriber = 0.

la userInputString = "brukernavn = Ralph \ 0" som NSString la commandString = NSString.init (format: "subscribe_user:% @ & needs_authorization = 1", userInputString) NSLog ("% s", commandString.utf8String!) // skriver subscribe_user: brukernavn = Ralph i stedet for subscribe_user: brukernavn = Ralph & needs_authorization = 1

Parsing av HTML, XML og JSON-strenger krever også spesiell oppmerksomhet. Den sikreste måten å jobbe med, er å bruke Foundation's native biblioteker som gir objekter for hver node, for eksempel NSXMLParser klasse. Swift 4 introduserer type-sikker serialisering til eksterne formater som JSON. Men hvis du leser XML eller HTML ved hjelp av et tilpasset system, må du være sikker på at spesialtegn fra brukerinngangen ikke kan brukes til å instruere tolken.

  • < må bli & lt.
  • > bør bli erstattet med & gt.
  • & bør bli & amp.
  • Innsideattributtverdier, noen eller ' må bli & quot og N', henholdsvis.

Her er et eksempel på en rask måte å fjerne eller erstatte bestemte tegn på:

var myString = "streng for å desinfisere;" myString = myString.replacingOccurrences (av: ";", med: "")

Et siste område for injeksjonsangrep er inne i URL-behandlere. Kontroller at brukerinngang ikke brukes direkte i de tilpassede URL-behandlerne openURL og didReceiveRemoteNotification. Bekreft at nettadressen er det du forventer, og at den ikke tillater en bruker å tilfeldigvis angi informasjon for å manipulere logikken din. For eksempel, i stedet for å la brukeren velge hvilken skjerm i stakken som skal navigere til etter indeks, bare tillate bestemte skjermbilder med en ugjennomsiktig identifikator, for eksempel t = es84jg5urw

Hvis du bruker WKWebViews i appen din, kan det være bra å sjekke nettadressene som skal lastes der også. Du kan overstyre decidePolicyFor navigationAction, som lar deg velge om du vil fortsette med URL-forespørselen. 

Noen kjente webview-triks inkluderer lasting av tilpassede nettadressesystemer som utvikleren ikke hadde tenkt, for eksempel en app-id: å starte en helt annen app eller tekstmelding: å sende en tekst. Merk at innebygde webviews ikke viser en linje med URL-adressen eller SSL-statusen (låsikonet), slik at brukeren ikke kan bestemme om forbindelsen er klarert. 

Hvis nettvisningen er full skjerm, kan nettadressen for eksempel bli kapret med en nettside som ligner påloggingsskjermen din, med unntak av å rette opp legitimasjonene til et skadelig domene i stedet. Andre angrep i det siste har inkludert krypteringsskriptangrep som har lekket informasjonskapsler og til og med hele filsystemet. 

Den beste forebyggingen for alle de angitte angrepene er å ta deg tid til å designe grensesnittet ditt ved hjelp av innfødte brukergrensesnittkontroller i stedet for bare å vise en web-basert versjon i appen din.

Så langt har vi sett på relativt enkle typer angrep. Men la oss avslutte med et mer avansert angrep som kan skje i løpetid.

Runtime Hacking

Akkurat som Swift blir mer sårbar når du grensesnittet med C, kommer grensesnittet med Objective-C til å gi separate sårbarheter til bordet. 

Vi har allerede sett problemene med NSString og format strengangrep. Et annet poeng er at Objective-C er mye mer dynamisk som et språk, slik at løse typer og metoder kan overføres. Hvis din Swift-klasse arver fra NSObject, så blir det åpent for Objective-C runtime angrep. 

Det vanligste sikkerhetsproblemet innebærer dynamisk bytte av en viktig sikkerhetsmetode for en annen metode. For eksempel kan en metode som returnerer hvis en bruker er validert, byttes for en annen metode som nesten alltid kommer tilbake sant, for eksempel isRetinaDisplay. Minimering av bruk av Objective-C vil gjøre appen din mer robust mot denne typen angrep.

I Swift 4 er metoder for klasser som arver fra en Objective-C-klasse, kun utsatt for Objective-C-kjøretiden dersom disse metodene eller klassene selv er merket med @Egenskap. Ofte kalles Swift-funksjonen i stedet, selv om @objc Attributt brukes. Dette kan skje når metoden har en @objc attributt, men er aldri faktisk kalt fra Objective-C. 

Med andre ord, introduserer Swift 4 mindre @objc innfall, slik at dette begrenser angrepsoverflaten sammenlignet med tidligere versjoner. Likevel, for å støtte kjøretidsfunksjonene, må Objektiv-C-baserte binarier beholde mye klasseinformasjon som ikke kan fjernes. Dette er nok for omvendte ingeniører å gjenoppbygge klassens grensesnitt for å finne ut hvilke sikkerhetsseksjoner som skal lappes for eksempel. 

I Swift er det mindre informasjon eksponert i binæret, og funksjonsnavn er manglet. Mangleringen kan imidlertid fortrykkes av Xcode-verktøyet raskt-demangle. Faktisk har Swift-funksjoner en konsekvent navngivningssystem som indikerer om hver enkelt er en Swift-funksjon eller ikke, en del av en klasse, modulnavn og lengde, klassenavn og lengde, metodenavn og lengde, attributter, parametere og returtype. 

Disse navnene er kortere i Swift 4. Hvis du er bekymret for omvendt engineering, må du sørge for at utgivelsesversjonen av appstrimmelene dine symboler ved å gå til Bygg innstillinger> Deployment> Strip Swift Symbols og sette alternativet til Ja.

Utover obfuscating kritisk sikkerhetskode, kan du også be om at den er inline. Dette betyr at ethvert sted som funksjonen kalles i koden din, vil koden bli gjentatt på det stedet i stedet for å eksistere bare i ett sted av binæret. 

På denne måten, hvis en angriper klarer å omgå en bestemt sikkerhetskontroll, vil den ikke påvirke andre forekomster av den sjekken som ligger andre steder i koden din. Hver sjekk må patched eller hekta, noe som gjør det mye vanskeligere å utføre en sprekk. Du kan legge inn kode slik:

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

Konklusjon

Å tenke på sikkerhet bør være en stor del av utviklingen. Bare forventer at språket er sikkert kan det føre til sårbarheter som kunne ha blitt unngått. Swift er populært for iOS-utvikling, men det er tilgjengelig for MacOS desktop apps, tvOS, watchOS og Linux (slik at du kan bruke den til server-side komponenter der potensialet for kode kjøring utnyttes er mye høyere). App sandboxing kan bli ødelagt, for eksempel i tilfelle av jailbroken enheter som tillater usignert kode å kjøre, så det er viktig å fortsatt tenke på sikkerhet og ta hensyn til Xcode notices mens du feilsøker. 

Et siste tips er å behandle compiler advarsler som feil. Du kan tvinge Xcode til å gjøre dette ved å gå til Bygg innstillinger og innstilling Behandle advarsler som feil til Ja. Ikke glem å modernisere prosjektinnstillingene når du overfører til Xcode 9 for å få bedre advarsler, og sist men ikke minst, bruk de nye funksjonene som er tilgjengelige ved å vedta Swift 4 i dag!

Lær Swift

Vi har bygget en komplett guide for å hjelpe deg med å lære Swift, enten du er bare i gang med det grunnleggende eller du vil utforske mer avanserte emner.

For en primer på andre aspekter av sikker koding for iOS, sjekk ut noen av mine andre innlegg her på Envato Tuts+!