De la minimizarea utilizării indicatoarelor la verificările de tip tare la momentul compilării, Swift este o limbă excelentă pentru dezvoltarea sigură. Dar asta înseamnă că este tentant să uiți totalmente securitatea. Există încă vulnerabilități, iar Swift îi atrage și pe noii dezvoltatori care nu au învățat încă despre securitate.
Acest tutorial este un ghid de codificare securizat, care va aborda schimbările din Swift 4, precum și noile opțiuni de scule disponibile în Xcode 9, care vă vor ajuta să atenuați vulnerabilitățile de securitate.
Multe vulnerabilități de securitate s-au învârtit în jurul lui C și prin folosirea indicatorilor. Acest lucru se datorează faptului că indicatorii vă permit să accesați locațiile de memorie brute, facilitând citirea și scrierea în zona greșită. A fost o modalitate majoră pentru ca atacatorii să schimbe în mod malefic un program.
Swift îndepărtează în mare parte indicii, dar vă permite să interfață cu C. Multe API-uri, inclusiv întregul Core API al Apple, se bazează în întregime pe C, deci este foarte ușor să introduceți utilizarea pointerilor înapoi în Swift.
Din fericire, Apple a numit tipurile de pointer în mod corespunzător: UnsafePointer
, UnsafeRawPointer
, UnsafeBufferPointer
, și UnsafeRawBufferPointer
. Va veni un moment în care interfața cu API va reveni la aceste tipuri și va fi principala regulă atunci când le folosiți nu păstrați sau returnați indicatorii pentru o utilizare ulterioară. De exemplu:
let myString = "Hello World!" var unsafePointer: UnsafePointer? = zero myString.withCString myStringPointer în unsafePointer = myStringPointer // cândva mai târziu ... print (unsafePointer? .pointee)
Pentru că am accesat indicatorul din afara închiderii, nu știm sigur dacă indicatorul încă indică conținutul așteptat al memoriei. Modul sigur de a utiliza indicatorul în acest exemplu ar fi să-l păstrați, împreună cu instrucțiunea de tipărire, în cadrul închiderii.
Indicatorii la șiruri de caractere și matrice nu au nici o verificare a limitelor. Acest lucru înseamnă că este ușor să utilizați un pointer nesigur pe o matrice, dar accesul accidental dincolo de limita sa - o depășire de tampon.
var numere = [1, 2, 3, 4, 5] numere.cuUnsafeMutableBufferPointer buffer în // ok buffer [0] = 5 imprimare (buffer [0]) // buffer tampon [5] ])
Vestea bună este că Swift 4 încearcă să prăbușească aplicația în loc să continue cu ceea ce se va numi un comportament nedefinit. Nu știm ce tampon [5]
arata spre! Cu toate acestea, Swift nu va prinde în fiecare caz. Setați un punct de întrerupere după următorul cod și examinați variabilele A
și c
. Vor fi stabilite 999
.
funcționareAddress (pointer: UnsafeMutablePointer) -> UnsafeMutablePointer pointer retur var a = 111 var b = 222 var c = 333 ani pointer: UnsafeMutablePointer = getAddress (pointer: & b) pointer.successor () initialize (la: 999) pointer.predecessor () initialize (la: 999)
Acest lucru demonstrează a stivuire overflow deoarece, fără o alocare explicită, variabilele sunt în general stocate pe stivă.
În următorul exemplu, facem o alocare cu o singură capacitate Int8
. Alocările sunt stocate pe heap, astfel încât linia următoare va depăși heapul. Pentru acest exemplu, Xcode vă avertizează doar cu o notă din consola respectivă devine
este nesigur.
lăsați tamponul = UnsafeMutablePointer.aloca (capacitatea: 1) devine (tampon)
Deci, care este cel mai bun mod de a evita deversările? Este extrem de important atunci când interfațând cu C să faceți limite verificând intrarea pentru a vă asigura că este în raza de acțiune.
S-ar putea să vă gândiți că este destul de greu să vă amintiți și să găsiți toate cazurile diferite. Deci, pentru a vă ajuta, Xcode vine cu un instrument foarte util numit Address Sanitizer.
Adresa Sanitizer a fost îmbunătățită în Xcode 9. Este un instrument care vă ajută să accesați accesul nevalid la memorie, cum ar fi exemplele pe care tocmai le-am văzut. Dacă veți lucra cu Nesigur *
tipuri, este o idee bună să utilizați instrumentul Address Sanitizer. Nu este activat în mod prestabilit, astfel încât să-l activați, mergeți la Produs> Schemă> Editați schema> Diagnostice, și verificați Adresa Sanitizer. În Xcode 9 există o sub-opțiune nouă, Detectați utilizarea stivei după întoarcere. Această nouă opțiune detectează vulnerabilitățile după utilizare și după folosire după întoarcere din primul exemplu.
Uneori este trecută cu vederea integer overflow. Acest lucru se datorează faptului că depășirile întregi sunt găuri de securitate numai atunci când sunt utilizate ca index sau dimensiune a bufferului sau dacă valoarea neașteptată a preaplinului modifică fluxul codului critic de securitate. Swift 4 capturează cele mai evidente pierderi întregi la momentul compilării, cum ar fi atunci când numărul este clar mai mare decât valoarea maximă a întregului.
De exemplu, următoarele nu se vor compila.
var someInteger: CInt = 2147483647 someInteger + = 1
Dar, de multe ori numărul va ajunge dinamic în timpul execuției, cum ar fi atunci când un utilizator introduce informații într-un UITextField
. Undefined Behavior Sanitizer este un nou instrument în Xcode 9, care detectează depășirea întregului semn și alte bug-uri de tip nepotrivire. Pentru ao activa, mergi la Produs> Schemă> Editați schema> Diagnostice, și porniți Metoda sanitară nedefinită. Apoi în Setări de configurare> Sanitizare nedefinită a comportamentului, a stabilit Activați verificările Extra Integer la da.
Mai este un lucru care merită menționat despre comportamentul nedefinit. Chiar dacă pur Swift ascunde indicii, referințele și copii ale tampoanelor sunt încă folosite în spatele scenei, astfel încât este posibil să fugiți în comportament pe care nu vă așteptați. De exemplu, când începeți să iterați peste indicii de colectare, indicii ar putea fi modificați accidental de dvs. în timpul repetării.
var numere = [1, 2, 3] pentru numărul în cifre numere de imprimare (număr) = [4, 5, 6] //<- accident ??? for number in numbers print(number)
Aici tocmai am cauzat numerele
array pentru a indica o matrice nouă în bucla. Atunci ce face? număr
arata spre? Aceasta ar fi, în mod normal, o denumire de referință, dar în acest caz Swift creează implicit o referință la o copie a buffer-ului matricei pe durata bucla. Asta înseamnă că declarația de tipărire va imprima efectiv 1, 2 și 3 în loc de 1, 4, 5 ... Acest lucru este bun! Swift vă salvează din comportamentul nedefinit sau din prăbușirea unei aplicații, deși s-ar putea să nu fi așteptat nici această ieșire. Dezvoltatorii dvs. de la egal la egal nu se vor aștepta ca colecția dvs. să fie mutată în timpul enumerării, așa că, în general, fiți atenți în timpul enumerării că nu modificați colecția.
Deci, Swift 4 are o mare implementare a securității la momentul compilării pentru a captura aceste vulnerabilități de securitate. Există multe situații în care vulnerabilitatea nu există până când nu există o interacțiune cu utilizatorul. Swift include, de asemenea, verificări dinamice, care pot surprinde multe probleme la momentul executării, dar este prea scump să faceți între fire, astfel încât să nu se efectueze pentru codul multithreaded. Verificarea dinamică va prinde multe, dar nu toate încălcările, deci este important să scrieți codul securizat în primul rând!
Cu asta, hai să ne întoarcem la o altă zonă foarte comună pentru atacurile de injectare a codurilor vulnerabile.
Atașările de formatare a șirului se întâmplă atunci când un șir de intrare este analizat în aplicația dvs. ca o comandă pe care nu ați intenționat-o. În timp ce șirurile pure Swift nu sunt susceptibile de atacuri de formatare de șir, obiectivul-C NSString
și Fundația Core CFString
clasele sunt, și sunt disponibile de la Swift. Ambele clase au metode cum ar fi stringWithFormat
.
Să presupunem că utilizatorul poate introduce text arbitrar dintr-un UITextField
.
permiteți inputString = "String dintr-un câmp de text% @% d% p% ld% @% @" ca NSString
Aceasta ar putea fi o gaură de securitate dacă șirul de format este manipulat direct.
permiteți textFieldString = NSString.init (format: inputString) // nu permite textFieldString = NSString.init (format: "% @", inputString) // bun
Swift 4 încearcă să se ocupe de argumentele șirului de format lipsit prin returnarea lui 0 sau NULL, dar este deosebit de îngrijorător dacă șirul va fi trecut înapoi la modul de lucru Obiectiv-C.
NSLog (textFieldString); // NSLog rău ("% @", textFieldString); //bun
În timp ce, de cele mai multe ori, modul incorect va provoca un accident, un atacator poate să execute cu atenție un șir de formate pentru a scrie date către anumite locații de memorie din stivă, pentru a modifica comportamentul aplicației (cum ar fi schimbarea isAuthenticated
variabil).
Un alt mare vinovat este NSPredicate
, care poate accepta un șir format care este folosit pentru a specifica ce date sunt preluate din datele de bază. Clauze precum CA
și CONȚINE
permite introducerea de caractere uluitoare și ar trebui evitată sau cel puțin folosită numai pentru căutări. Ideea este de a evita enumerarea de conturi, de exemplu, în cazul în care atacatorul intră "a *" ca nume de cont. Dacă schimbați CA
clauza la ==
, aceasta înseamnă că șirul trebuie să se potrivească literalmente cu "a *".
Alte atacuri frecvente se întâmplă terminând șirul de intrare devreme cu un caracter unic de citare, astfel încât pot fi introduse comenzi suplimentare. De exemplu, o intrare ar putea fi ocolită prin introducerea ') SAU 1 = 1 SAU (parola LIKE' *
în UITextField
. Această linie se traduce "în cazul în care parola este ca orice", care ocolește autentificarea cu totul.Soluția este de a scăpa pe deplin orice încercări de injectare prin adăugarea propriilor dvs. citate duble în cod.În acest fel, orice citate suplimentare de la utilizator sunt văzute ca parte a șirului de introducere în loc de a fi un caracter special terminator:
lasă interogare = NSPredicate.init (format: "password == \"% @ \ "", nume)
Încă o modalitate de a proteja împotriva acestor atacuri este pur și simplu să căutați și să excludeți anumite caractere pe care le știți că ar putea fi dăunătoare în șir. Exemple ar include citate, sau chiar puncte și tăieturi. De exemplu, este posibil să faci a director traversal atac când intrarea devine transmisă direct către Manager de fișiere
clasă. În acest exemplu, utilizatorul introduce "... /" pentru a vizualiza directorul părinte al căii în locul sub-directorului dorit.
permiteți utilizatoruluiControllerString = "... /" ca NSString să se lase sourcePath = NSString.init (format: "% @ /% @", Bundle.main.resourcePath!, userControllerString) NSLog ("% @", sourcePath) Produsele / Debug4.app / Contents / Resources, acesta va fi Build / Products / Debug / Swift4.app / Cuprins permite filemanager: FileManager = FileManager () permite fisiere = filemanager.enumerator (atPath: sourcePath ca String) = fișiere? .nextObject () print (file)
Alte caractere speciale pot include un octet terminator NULL dacă șirul este folosit ca un șir C. Pointerii la șirurile C necesită un byte terminator NULL. Din acest motiv, este posibil să manipulați șirul prin introducerea unui octet NULL. Atacatorul ar putea dori să termine șirul mai devreme dacă ar exista un pavilion, cum ar fi needs_auth = 1
, sau când accesul este activat în mod implicit și este dezactivat în mod explicit, cum ar fi is_subscriber = 0
.
permite utilizatorInputString = "username = Ralph \ 0" ca NSString let commandString = NSString.init (format: "subscribe_user:% @ & needs_authorization = 1", userInputString) NSLog ("% s", commandString.utf8String!) // prints subscribe_user: username = Ralph în loc de subscribe_user: username = Ralph & needs_authorization = 1
Parsarea șirurilor HTML, XML și JSON necesită o atenție deosebită. Cea mai sigură modalitate de a lucra cu ei este să folosiți bibliotecile native ale Fundației care furnizează obiecte pentru fiecare nod, cum ar fi NSXMLParser
clasă. Swift 4 introduce serializarea în siguranță pentru formate externe, cum ar fi JSON. Dar dacă citiți XML sau HTML utilizând un sistem personalizat, asigurați-vă că caracterele speciale din intrarea utilizatorului nu pot fi folosite pentru a instrui interpretul.
<
trebuie să devină & lt
.>
ar trebui înlocuit cu & gt
.&
ar trebui să devină & amp
.„
sau '
trebuie să devină & quot
și & APOS
, respectiv.Iată un exemplu de modalitate rapidă de a elimina sau de a înlocui anumite caractere:
var myString = "șirul de dezinfectare"; myString = myString.replacingOccurrences (de: ";", cu: "")
O zonă finală pentru atacurile prin injectare se află în interiorul operatorilor de adrese URL. Verificați pentru a vă asigura că intrarea utilizatorilor nu este utilizată direct în cadrul procesatorilor de adrese URL personalizați openURL
și didReceiveRemoteNotification
. Verificați că adresa URL este ceea ce așteptați și că nu permite unui utilizator să introducă în mod arbitrar informații pentru a manipula logica. De exemplu, în loc să permiți utilizatorului să aleagă ce ecran din stivă să navigheze pe index, permiteți numai anumite ecrane folosind un identificator opac, cum ar fi t = es84jg5urw
.
Dacă utilizați WKWebView
s în aplicația dvs., ar fi bine să verificați și adresele URL care vor fi încărcate acolo. Puteți să vă suprascrieți decidePolicyPentru navigareAction
, care vă permite să alegeți dacă doriți să continuați cu solicitarea adresei URL.
Unele trucuri cunoscute de webview includ încărcarea schemelor URL personalizate pe care dezvoltatorul nu le-a propus, cum ar fi un app-id:
pentru a lansa o aplicație complet diferită sau mesaj:
pentru a trimite un text. Rețineți că vizualizările web încorporate nu afișează o bară cu adresa URL sau starea SSL (pictograma blocare), astfel încât utilizatorul nu poate stabili dacă conexiunea este de încredere.
Dacă vizualizarea web este pe ecran complet, de exemplu, URL-ul ar putea fi deturnat de o pagină web care arată exact ca ecranul de conectare, cu excepția direcționării în schimb a acreditărilor către un domeniu rău intenționat. Alte atacuri din trecut au inclus atacuri de scripting care au scos cookie-uri și chiar întregul sistem de fișiere.
Cea mai bună prevenire pentru toate atacurile menționate este să vă acordați timp pentru a proiecta interfața dvs. utilizând controale UI native, în loc să afișați pur și simplu o versiune bazată pe web în aplicația dvs..
Până acum, ne-am uitat la tipuri relativ simple de atacuri. Dar să terminăm cu un atac mai avansat care se poate întâmpla în timpul runtime-ului.
La fel cum Swift devine mai vulnerabil atunci când interfață cu C, interfața cu Obiectiv-C aduce vulnerabilități separate la masă.
Am văzut deja problemele NSString
și atacurile șir de format. Un alt punct este acela că obiectivul-C este mult mai dinamic ca limbă, permițând să se împrăștie tipuri și metode pierdute. Dacă clasa ta Swift moștenește NSObject
, apoi devine deschisă pentru atacurile de tip Obiectiv-C runtime.
Cea mai frecventă vulnerabilitate implică schimbarea dinamică a unei metode importante de securitate pentru o altă metodă. De exemplu, o metodă care se întoarce dacă un utilizator este validat ar putea fi schimbat pentru o altă metodă care va reveni aproape întotdeauna la adevărat, cum ar fi isRetinaDisplay
. Minimizarea utilizării obiectivului C va face aplicația mai robustă împotriva acestui tip de atac.
În Swift 4, metodele pe clase care moștenesc dintr-o clasă Obiectiv-C sunt expuse doar runtime-ului Obiectiv-C, dacă acele metode sau clasele în sine sunt marcate cu @atribut
. De multe ori, funcția Swift este numită în schimb, chiar dacă @objc
este folosit. Acest lucru se poate întâmpla când metoda are un @objc
dar nu este niciodată chemat de la obiectivul-C.
Cu alte cuvinte, Swift 4 introduce mai puțin @objc
inference, astfel încât aceasta limitează suprafața de atac comparativ cu versiunile anterioare. Cu toate acestea, pentru a suporta caracteristicile runtime, binarele bazate pe Objective-C trebuie să păstreze o mulțime de informații de clasă care nu pot fi îndepărtate. Acest lucru este suficient pentru ca inginerii invers să reconstruiască interfața de clasă pentru a afla care sunt secțiunile de securitate pentru a patch-uri, de exemplu.
În Swift, există mai puține informații expuse în binar, iar numele funcțiilor sunt modificate. Cu toate acestea, manglingul poate fi anulat de instrumentul Xcode swift-demangle. De fapt, funcțiile Swift au o schemă de numire consecventă, indicând dacă fiecare este o funcție Swift sau nu, parte a unei clase, numele și lungimea clasei, numele și lungimea clasei, numele și lungimea metodei, atributele, parametrii și tipul de returnare.
Aceste nume sunt mai scurte în Swift 4. Dacă sunteți preocupat de ingineria inversă, asigurați-vă că versiunea lansată a simbolurilor de benzi ale aplicației dvs. merge la Setări de configurare> Implementare> Simboluri Strip Swift și setarea opțiunii la da.
Dincolo de obfuscarea codului critic de securitate, îl puteți solicita și pentru a fi în linie. Acest lucru înseamnă că orice loc în care funcția este apelată în codul dvs., codul va fi repetat în acel loc în loc să existe numai într-o singură locație a binar.
În acest fel, dacă un atacator reușește să ocolească un anumit control de securitate, acesta nu va afecta alte evenimente ale acelui cec situate în alte locuri ale codului. Fiecare verificare trebuie să fie patch-uri sau agățate, ceea ce face mult mai dificilă efectuarea cu succes a unei fisuri. Puteți să codificați în linie astfel:
@inline (__ întotdeauna) func myFunction () // ...
Gândirea la securitate ar trebui să fie o mare parte a dezvoltării. Simpla așteptare a siguranței limbajului poate duce la vulnerabilități care ar fi putut fi evitate. Swift este popular pentru dezvoltarea iOS, dar este disponibil pentru aplicațiile desktop macOS, tvOS, watchOS și Linux (pentru a putea fi folosit pentru componentele de pe server unde potențialul de exploatare a execuției codului este mult mai mare). Aplicația sandbox poate fi întreruptă, cum ar fi în cazul dispozitivelor jailbroken care permit rularea codului nesemnificat, deci este important să vă gândiți în continuare la securitate și să acordați atenție notificărilor Xcode în timpul depanării.
Un sfat final este de a trata avertismentele compilatorului ca erori. Puteți forța Xcode să facă acest lucru mergând la Construiți setările și setarea Tratează avertismentele ca erori la da. Nu uitați să vă modernizați setările de proiect când migrați la Xcode 9 pentru a obține avertismente îmbunătățite și, nu în ultimul rând, să utilizați noile caracteristici disponibile prin adoptarea Swift 4 astăzi!
Am construit un ghid complet pentru a vă ajuta să învățați Swift, indiferent dacă începeți doar cu elementele de bază sau doriți să explorați subiecte mai avansate.
Pentru o ameliorare a altor aspecte legate de codificarea securizată pentru iOS, verificați câteva dintre celelalte postări de aici pe Envato Tuts+!