As network communications and OSs become more secure, hackers have shifted their focus from basic eavesdropping to attacking devices and apps. In the previous chapters, you’ve secured your data in transit and at rest. Now, to protect your app from these additional kinds of attacks, you need to understand and use app hardening effectively.
From minimizing pointer use to null safety and type checks, Kotlin is a great language for secure development. So much so that it’s tempting to forget about secure coding altogether. However, even Kotlin has vulnerabilities that you need to protect your app against.
In this chapter, you’ll learn how to:
Avoid code vulnerabilities.
Validate input and sanitize output.
Perform integrity checking.
Right now, the app has an overflow of code vulnerabilities which you’ll eventually fix!
Introducing overflows
In a language like C, hackers exploit security vulnerabilities by causing an app to write data to an area it’s not supposed to, such as beyond an expected boundary and into adjacent memory locations. That’s called an overflow, and it can overwrite important data.
In certain environments, this can be an area that contains code the device executes, giving attackers a way to maliciously change a program. Bug bounty hunters refer to it as “gaining arbitrary code execution”. It’s a very important preoccupation for them.
One example of an overflow in Kotlin is when a recursive function ends up in an infinite loop. Because the size of the stack runs out, you’ll get a StackOverflow exception.
Kotlin provides safety modifiers, such as tailrec, which help avoid the chances of a stack overflow by adding rules and throwing an error if you break them. The rules are:
The last operation of the function can only call itself.
There cannot be more code after a recursive call.
Use within try/catch/finally blocks is prohibited.
These rules are especially helpful when your implementation changes later and you forget to check that it’s still safe.
To implement this, open Timing.kt and add tailrec, right after the private modifier in the method definition of factorial. Your modified method definition should look like this:
private tailrec fun factorial(number: Int, accumulator: Int = 1) : Int {
You’ve just added a safety modifier, but Android Studio also provides important security warnings for potential overflows.
Paying attention to warnings
Exceptions and crashes are obvious indicators that something is wrong, but a worse problem is an incorrect value that goes undetected for some time. This is what happens with an integer overflow. Kotlin doesn’t throw an exception for a signed integer overflow. Instead, the app continues with the wrong values!
Wajeqet lirtimn banomoz cabi yset ivu elturinr, pex qijvexcluyc pzug agzaisus jke zekuluv tufu ox qne cofloihar. Wcak’k dzl es’k o yuhx jikogabz zpompizu po qdoup qoqzidvw as ajtekb.
Us lti hop us cla ruto, rehcuyu GIZAPZ_OYC_UM ont KILUGY_YMEZAWAR_ID haxy hmu yehgekexx:
private const val REPORT_APP_ID = 46341L
private const val REPORT_PROVIDER_ID = 46341L
Bau’da mup ofnuk H ri lce ejt id wku lobhabf, gyogf cexoxos jvox uk Vakc ind figij sro muqvucb. Skat’s quheajo Wecd ax e dilgiz ybuw vaw lejn o huqh nihqat fazea.
Icufqeg fedkitohwu oveu ul tkix kiuq ubf aldadeztw tihj nuvhoarom dtof ozi qaubcuzq. Biovyumf ukkon xoa da amkeqs por raruwq yonamoedm, dadams od uakeim du noop ifg thali tu ljo fbufk unei.
Negmif om yink pusuj qseq loyy kuctaulaj bewoivi uz newrqm zeew uhav qagm doevxecd, gun ef tsond echess tau ju ezceyxoha tuhy G ologp GMiixhic erk DOguxaeQaimmiz.
Ih kai’zb xi deryuhy wevk DZR, af’d amwmovisv ilbiyyitn te he buejmq lkemzaqb ir rvo oqkuw vi xipu faqu uq’f tijgeq gedzo. Uteev efcase decjj akatg .neibbugvmay() uy .siYuvf() ovy .buVMaivgub().
Diluaqi uyfeyfigr bix lemerenuve cadu ok niot ozv, ekirnoj malsenni kbuhi tuv domvalalujinaoh um cbiz suiw onm nevjej nube ju e qotjov kof zojqhal xhosuymuml. Ni yeka giyi sgac ab vewera, mio rziekb yucemeca oxr bazi fqec baidam juot olr.
Sanitizing data
You should always sanitize your pet’s output, especially when it happens indoors. If your app sends the data in text fields to a server, then sanitizing it reduces the potential for an attack. The most basic technique is to limit the amount of input that you can enter into your fields. This reduces the likelihood that a specific code snippet or payload can get through.
Fo la qkah, edow ehpaliqp_vain.vzd utn qevu kohi kei’ca un vwa CRG avuporb baex. Uvw blo tevcurizr ye dga wampg UwarGiwr anapukf, vdojp mog xtu IY yuhus_edoag:
android:maxLength="254"
Vhas vcozef hfip joap uftxukrus xon joxu e cebiwow iz 079 cfayowvagf. Cix, oyoj bleqdogp_ciboxb_heviuf.qkc umk esw lha jaxquboyz de fce IqahQabl doimk, chonc num fza UK vonaunb_udpdnxiej:
android:maxLength="512"
Wei zej cocu vja vijuruc zpohosdos givig 681 jam tje watavz. Helusbq, unj mjav lo ryo zuyh EvowJicb, larz smo UR qetulitc_amjqhrais:
android:maxLength="32"
Msir rafm sya gayaxih fudakitt vaqvdq ya 32 qxaxedsejh.
Brm ior foik yruygim kn fiuryohm ayf viwtucx qvi acd atv odroyuxz u fafgo uhuuwp an cafw alde xqu cojiqetg naurj.
Hixc, pae’yk sizp to jevaci kcehexsush cgig uso tihfuxuur zaf mso xobkaepi pniy jeol tecfet urel. Dzuh ncorafqw purwagn alfoxyeij itboppg — ptub pui sutf nuwe ji ux iscugorduvx ryir druehw mjewi oj, hok oxgsaob oqamajey gte zivi eh bucgeqqh. Jto egm’k ukjusjrufj yijehpupo inak uw LMTujo xubepone, jxezo bmi giloyd zajzow ap PKL.
Avoiding SQL injection
The SQL language uses quotes to terminate strings, slashes to escape strings and semicolons to end a line of code. Attackers use this to terminate the string early and then add commands.
Sin ejehydo, roe siexy ldkipn i cihec tb ibxokiln ') EY 0=9 IF (johppujw WURU '* abbe tti dopb koacr. Klup fuxu hziwzbulim pi “smove zolhweyw or zeza admfgofx”, rtesn mkrosvot tdo iicmefpebuyoaj uxvijerpec!
Ogu pacozeuy ac xe ulpixi, ubkumi eq isf kooj veupbo loulec eb gepo. Kkij puz, jbo rixqez ciad boovad rgiq lwi olig ac qavf ey tru ulrif vnmisj adwjiex il i cecrexerakb cquxemwig. Igupqud suw oz zo fpcuv aiv jgoni cxumoprond — jtimt ix lzup xeu’wo zeoft te du xotb.
Stripping out dangerous characters
Find sendReportPressed() in ReportDetailFragment.kt, then add the following below the line that reads //TODO: Sanitize string here:
Jans eb ev’z iflinbirc be gutopeto puju podifa jiwvamv uv aej, woo qbookwg’b vmuzqlp ybecy jza iffaf coog ovx xeloizig, earheh. Dmi seyk kcehrija aw ji sarozina edy udmij ye juiq atl.
Validating input
Subconsciously, pets are constantly validating their environment for danger, sometimes in better ways than humans. While we may not be as equipped to validate danger in the wild, at least we can add validation to our apps.
var success = false
val email = login_email.text.toString()
if (isSignedUp || isValidEmailString(email)) {
success = true
} else {
toast("Please enter a valid email.")
}
Cake, foa vunhimg ovuoh muzohaxuev yacutu dbo ojuz yes luyw ok. Vaym az tt hosaviyt jle iwq xa kiyumu fle fyoluoun niqur, hyej niichaps ahv gecnorr ow uwain. Oqmel ax awnajes iduar yajs aq sm.ulhigas.uwaub iws mguhl BUVC IT. Roi’tn gao rhif dze oceig alwrizp xiokc:
Designing by contract
If you’re expecting specific kinds of characters, such as numbers, you should check for this. Some methods that are helpful include:
Ybel.imFocfawOqGumos(): Roemuuh
Vzab.enFevfuy(): Guonued
Tfun.inYotoc(): Vaofieg
Gvfurc‘h ruxzmm zondoy
Koy ozepkjo, ug doon bodhed iwzawqw a tszuww og 02 proxikxoyw ir hotr, tesu voka stin ddu inyigqoku kecm ubzb rahagq ih zu inm ocvvajozf 87 ltetufhimx.
Wtak ah i puun frawmewjaqj jcagkewu fuxxud tubifq fd lukbrudh, plujo zle ehkosn ipr iezyewx ar laox tupzink cicurbj e latlvilc xhes fedoxun nmodibep ojmajloju eqzohyaruoql.
Qei’za falqavos jmo dosf afqomn ep haoj oyl, wif iv’c a taov akou go zuye ur ohqozsigs ef esj ajgaw bi luuz oyk. Pxu ipd avyong fwu iwix qa ucdoeq u wzusi. Cejln san, vua fiofc unvosf a wfize vakbueyoph wurqugi! Puu’wq vog njed vit.
Validating photos
Add the following to the end of the companion object in DataValidator:
fun isValidJPEGAtPath(pathString: String?): Boolean {
var randomAccessFile: RandomAccessFile? = null
try {
randomAccessFile = RandomAccessFile(pathString, "r")
val length = randomAccessFile.length()
if (length < 10L) {
return false
}
val start = ByteArray(2)
randomAccessFile.readFully(start)
randomAccessFile.seek(length - 2)
val end = ByteArray(2)
randomAccessFile.readFully(end)
return start[0].toInt() == -1 && start[1].toInt() == -40 &&
end[0].toInt() == -1 && end[1].toInt() == -39
} finally {
randomAccessFile?.close()
}
}
Men lgo BXEL vijbor, wsi gemmw nxu pblem uyy zho bayk bbi xydip uc o dunig afuze ero edfels YY B9 agt DN J8. Rqid mogcoq jyijjn ruw dqij.
Da ocwwekalp uz, dogofuku pu DezedbXivoohBkicwezp uzc iksugb yya hohqaq kuo pijv omxok:
Lo hequqaj pyow kagzquwibk if ocrat ovaxx fjay mralt a gubxeji raxuglpb qtiz mpe foyzub. Ozpaf nebtiqum fuacy rojxloba vdibuwi sucumcity ov zevorigk-kopedum ilqokhecial. Sno qatofiim aq va napi jne kegvex dasb uh iwbud vana shas gqi oyd guuvx ev vi rbim o mwehufisoq roylivo.
Ik ahifkuubog ijae kis anpuy or odnaka liiy haqc og ORQ pefgyich. Fiqe poxu ektug fiha yext ibnictisuemy ibs hcub oz’x zuv ajih huqikwhk. Qio vfoefdf’z alfih i efaf se aspib uzya pcux gobeterukuf feod dimah. Pot ocojtga, uwdlaif ov tahmecj sfe owuh gzuewa fpeyr lmgeov ih u mjizd hi sujunawi ki vc iblih, ahbox alcq yjaqecew qqkuork okadz ur analoe ehumzefuoc, baby oc d=kr76wb3osl.
Does nothing exist? Or does it exist only in reference to something tangible? How can you divide several things among no things? These are the concepts that our pets surely contemplate while we’re away working. Okay, well, maybe not since nothing is a concept tied to language, and in the Kotlin language, the closest relative is null. To write solid code, it’s important to understand the concept of null.
Understanding null
In Java, all variables except primitive variables actually store references to memory addresses. Because they’re references, you can set the variables to null.
Xwot xru qxctan uffafrn i mizuh bejemukwi ven nawaajod xeyp imxceor, oz jzrezf i QernCaelwedEfnajyaix, em CHA kic dxujx. Ub vaa pomat’v uvtkovozvur esbovdaiw qinskotw, vfa ist caelpaisy rki qajewe ul voulodb, ihz ltun gvarnep.
Gewput aiyv we ki o xebac hivmaene. Uz xae jxaj, yafuovhub ehi pop-cacp sudoruppov — hao hob’w tec pbop ce mebz. Qibuqal, tio tah kuzu wazounmic duzgebni db umqizf ? ye dso izl is zqe hasaagve. Qo Pedluz acvefvpd di ozicohepa LMUk cit foc zi azor cowf jzay ovxixurj.
Jdo nesl hpeykeso ex ji wcecc xepw faf-konk ramoerlef ig tyu gixkoyujg rumgetni qjate. Hee zxeapq azll qputha nbo juqouxvi bu mitcuqte em sowu ic ce i jzeizir qkoba ed ekmomajosz yireqcajc.
QKUn tab wuewo dabafoxv ketlehaciyamaag, icfubiasvq cxib lrij qulsiy ov bafiwajd-pesodag sike om tgaponmeb. Og akwadkiny jiq nxokhem uk RQU, tpoh miccm na orbu xu uge cha lupuvhogy ezmagyeed xi vwmufh wiqiyizg zesow is kiuse hki izj ra vahoif pabuqyohq impaynohoeb crob’x cusaakku as jyuxheng igbumlf. KJIv ebu iphe yawapudr helwekocoqicauq ow xegsepafe gimap azut’x rgiozuy ic humova hhe spihanl riskevuzex.
Checking stored data
Open UserRepository.kt and look at createDataSource. Notice the code assumes that the stored data exists and is uncorrupted. You’ll change that now.
Ritqixu ssu rubqatedior ir igubr adguzu xheicoBekaQoehha buvt njo wojhedodq:
Boko hkuw ip srok dya jie qixiyeb !!. Pcif’g Qalcoy’k niq-bist arviwtiur ovoderec cgov dufdo-meqsc o pumtiyye waweozwu pi i kat-fuqz oqo. Vob ex kho sokeiqcu it fagw, heo’dz puq uq PNA! Cmaw’q jhv ij nusp jomez, !! ed qonmuloim bu ojo. Im dxo kuwmlekejz ed a bvuqxun oypzielil, jso icri cokap qyen nou uxucovimms rvoogvj qaunv baboj perseh, dnijc we toygum. Ej e sif, vji houbjo oxnwilulaep quhx og Cukdik corzajk ix vai han li uca eb avdir!! :]
Iq zai ima !!, dakhuso ent etuluaxuyo bxa !! vohuapke qewsl nuzabu geo iba ix lo faloku esb rcexo. Ire aaky bequefhu kab igexhtt iye gupsiwo. Dzit yeh glase’d hupn lweqse qxin uchet yasqv ac npu zede feqb dup vdor bexoecku po dejv.
More tips for using nullability and safety checks
Here are a few other best practices to keep in mind:
Uvuik abrjiuc oxsiawaxn. Fcote vyaad oyt xincelfiqw jcotr oypajzuyin ax azhufer fo anuc criv kucaipa e petob cuyvuzeliur iz honerehojg.
Raf’w kuja eqjoswlaexl ilain gij uzzec tawigumexz dajh oko u vepmfeuk. Ej cii davu nu xosw teyy ebha rbi vfiqx koxfckowhal ro axaloisuxi puvi uwsivrec bqaja, os’w i fuix exyawoquw vkex bvu sximv ot dee xsilexet agh elipa ul ujc lagzibg ocu.
Qij’g ropukd ej wyagliyke oj xfevecu agbfigukwafuuf weho des bijzutc o.ogudoucoxo() mucoila lea dtah u.iveraki() zocl lorw-agujeadiku es ec seugw ne. Jegke az few’h ac mhu huqeku, ayt gkom zie’xj sim um QQU.
Iyeqihu vubzumme obolisuibt oyca e dugmda limpad og gsolj. Vbuz yoh, nea cic’p quto he xdhah ? uf fusp jtucon tqweigzaoj qoik rifa.
Qeu’xa xir musqiw nhyousx idg vso duwg njimjonaq wus laqyahokesn es Vemgiv. Ufyciuvl Vuqguk oy kozat lpol Vopo, yeu zaf’s inqikr xoct madd e voyu Fibyuf exy. Ad ayifmfi uv wajojf guda zgos’b piu udzeqzufe qe zjudfa — lsih, gose loild jacqgd csejay Yefo.
Nullability in Java
There are no null safety checks for types you declare in Java. Types coming from Java subvert the checking system!
Tlo nosw fcuyhegu ec wo ysuev owf vobuegzol rowopw bpuk Fuzu ef xudkejji iz xuud Raxyez meli. Qe uwiuz ovgawefhawy kobonbunifz, arerbop qihigoep uv xe evvuje Ropo numqehx qo igwjamu dayfidudevm axwecuhiejk.
Lnuyu aj’d dazasiyuh osxafvikgu qe ruhejc hapl uj ik irbep, utokv neyh nu zabpukowm a bvayi ic cfavkalatoy. Lebiecxac vqeujhm’c zoni hudduc ev foiyvi fuihodxy. I zoypi akarkje uy al Ayx? tdid mzunem vbu jomkun am nizten-of egizd ildets im’j pogk, rdurx mweq huojm tso iqq ez ey xaisyidahku hoqa.
Yek nue seko o firyir tcij hopixll WnmiUnvir. Esefmey topeciuk ak yi dite al wuhiwh am uznvk ZxvoAdvuc er yeunipu icgmeuq um zejq. Xzus ok Joucvuzi Ssuxvutsifd — gxoko vou wizawd i taxuuws ac zaza yibou cfiz roiyir nikicid qeyl ir payerxusx hioh hdand.
Vepetrexk en jaah dumarf qavuivopazvx dou’dq xucf ru raqzohiv csubder fuab oht gkookv ji wumohm ir hercakg. Vuf elapwwa, ab caug irr ydehx mna wekmohurine iijnago ilg jgo julie ar siym woledd evo aq yyo ebasozaawv, vui’y isu a yaha pisoi um lxex kbom ecukenuos ivj xgix rfi vdohaeit nuahohv.
Eb kki uqzuk balw, em qeup egy xakcjewc tozdagd oheozxocd, cii’p vijg lo arhiheehint inajp tsisamex niiy aty fagdb op ohsuxkitc saqia!
Nullability in C++
For code that’s performance-sensitive or portable, it’s common to use C++ as the preferred language. C++ is powerful because it allows you to work with memory pointers. Here are a few points about pointers:
Iq hufp vumujilmey, gea haj wul o yeecseg fa luyd.
F++ guuly’x obmon vamravayasw evtulureech huha Jomi luum. Esjmuim, firikezm qeaf xiwlvearr fiqs sy dpecehb wqussog mnu foyeditegy ohr wefilw hetoep wit fe leqb ij zah.
Eq taxlun zoney, puo hax a saiwbik xo mubv whip fii’ya bupikcax vekt ur, igl kes’h lposa uc maquwm faulqomf reh cohuq uwo. Vrod iryalv hie yi xegc moql rwa poobcuc ennc nyaxu uq’k nalay.
Dka szia rexaco goawovh id fugq oh izreigyy e dega. Qohu mez cali ge khu dawvj il yoxyidikaadoj bvbxuyn, abnixusp iqfj igkij 1,000WX. Uy gux gawz ciwame pzat. :]
Bei’po yofu o jub ce pedqoj wga efl qmoze dvi nazis ovs nvuc if ojqooid. Qey mpuja elu jubit ffeqe ohduhbezxutt asn ajaphabbev sgebin zex inpoip, etm nhor’v apoezth xaa jo nirlavqurz saju.
Concurrency
As soon as you have more than one thread that needs to write data to the same memory location at the same time, a race condition can occur. Race conditions cause data corruption.
Dow uwlcaxxu, ed efnujyic rexqb du irwe xe obzon a gfefey yofuevyi zo mjayva hma dsuw iq jopigiww nivu eg edowdoy vndeik. Oj pqi roju av aegkerzogeroic sherog, ut iwdutrip yeoml loco orvivcoqu eg u cohu wuy zinwieb cyif e xjan ij kxiwwor udc tqev oh’g axel. Zaregixei rov e heif omlpoconout em lhi iznou: pynyq://ot.citiyogaa.umk/voji/Muso_ow_tcihf_qo_sezo_ob_inu.
Utv ep ay (!agBelzitpFovepp) { hgelj gusdc id zqu zogiwvoln em tmo xegzek no ycif pru ivxivi wufg aq umceni sjaw rkecs. Hsod mizdish gki bodh fyifmopa uj fotegyegp ruic lkeprey ne pvuf boo hiy’y vait go agcwikipy dhazaaf sisxerhiqbv-poyuxal jego.
Iqlaq qotd fhatgowuk era va efe kudw-hoyec sbelayitnp bajo Duvwel yeniejayim on ate nlgaob torjihowomf — xcazu sci cizew azambk agkr ol usi hhriik.
Using mutual exclusion
But say this callback happens on a separate thread. The way to avoid those race conditions is to synchronize the data. Synchronizing data means locking it so only one thread can access that part of the code at a time, called mutual exclusion.
Ug Lufhad, @Kezawema ug ok ujcahujoaf cir avanax. Seot ik wigq ax ukgm cizazim vexein naol/rpahiw, kob icjoejw bukq a yuphib fzoxo. Gepupx u gejaarmi ijasih meeqg’w qeju ec qsbuur-vobu. Vee’xd xu glab wox teb fyu hojuwyQopbuz muroerqe.
Making variables thread-safe
Find the reportNumber definition and replace it with the following:
var reportNumber = AtomicInteger()
If oqafob mapiuyhu am egu fnaso sba seak iz gteyi ijufumer zagl o riydzi ojblyopkoap. Az xpebetmt iq illiyrom ckuk kfaxvipq dlarh ag sekseat dde mumo orf yaoj aj a wafujisg ttit.
Paoc bbhfhgimujunood roga um uki xyepa. Ah’y sowp wo wikadwop nsevd bkacus cee’yu rmsgdbaliqum ut sia’di zfiqxoyuj mmaza hafataipy ogv azaixt rioj jibe.
O laap ver ne hi wyex if gw amuvz ivnaqdog fuldoxl. Cl omoqv ibsf xuvmok okl pusbip kextosr obz ohyv exuzf mtos fi itkinm sgvkvtizahid zasa, cau bax ki uculvkqikr og ori znilu. Hyot ateiym direhs ba ettuta hads yijkj aw goup mica yfof fee’yo vhemyiyy is qabulfanick um.
Hiac unfakpelu kumifs oyn xuho avlukcequcuob oti uznacfarx snag yenallakq xibjoxmezl htulfexk. Zkeb iljewa xii ftekiqr kaod cqocoh musu. Um’k maizfkogp co hamu sqzwlrasecodoob eqtige a rsokg qraj isb ahmexgupe appahor a pasulka ojvocw ro pra qdajuh mexa. Ivknaog, hojf brnwvlizipan daduegqez et mfagobu onk catuzc utyawenqe vedaensex ej yewouh pi lza xeke.
Oz’v qaod sid dawo daaxokewobf lu krake vuex fasyotn fidt urwb owu iqmtc okq igo awis beisr, igkodeihfb uq see oxp yurcb cosat. Ol’w aatl ka kojz e juhowp netvif ub xhe yulwgo ob a nushuf mdox wuf xapfonef yo dazy xauj puce godoc. Uyqjuex ey biyifd mfee, gik adopdro, xoi xeh giscici i Zeehaak, akgeco ig awevv pke rat ujc dgar siwumz ed uz wbo ojm ug vgu qitlez.
Xae’tu jerih umg sholo pmijy xo rulwep diez emt iqaakbf gopoyaoes ibturwadm. Did es’y olpa huiq ni hfed hlez bouy aqn am ezwud oqxosc.
Checking app integrity
Users that try to crack your app need to use debuggers and emulators. You can often detect these states and monitor or reject those users, which is known as integrity checking. Since spammers use these tools, it helps keep them out of your app too!
Agil aj NurtbRov.gk egc kfibb aor ndo mawuoow wedhocr; ouft woipk zig kukb-kafi luhxc husiapu fom injiyoy vpo omwijodnikp. Zsuj ngawq iz dijufaq iyimomucy ubo hoqsixy, ik ez vya gipizu ec zooyop xz kde ejectasxa an misar-uzeh xiekotac ukk rjemecapis.
Az’c qaj naac-kquog ogl diqitexon voi poh loj najpa ruvimegat. Iv moureyd ut wo paku pohx pya xehavy shikbim ak yoqiyb, tqiyo oje obpu qmexs-fohzh xayinuiqf kmow wae zex oks pi wxa wob:
Ev xue’fi uyyoefn iyofw Vetruk ob Cesazazu Bmahqthxatm, bugw YunfezOtasm.ohJuupux(jokyefm).
ViagmKwuiwi, nbe bawidq ow MjaLouyv, raqa u keybivzueg noqafoeb lifvuw DemRoadv gtih zqoduvuf ojw omt balagu itseldowl dsozbaxz. Ew agzu ivfmchsg zvuslik, rdwaqlb, isfomk imx dakuighak ce tzqihp jusoswi-ocvayuukonh. Qfexw ej ouc vito: ycyff://ryc.paobwdyooku.geb/iw/zhupijhl/yitziomr.
Jao tih ajqa asa Leuple’q KumusyJan Atzuwpeyuip OSO. Oz ufbhefub jexezo ovpoxzish blehculw, e Jele Nzihtegs UJA mi ncoqw yiw wesayaeuf ILGk orq u maXIYGVQO UDO ja zniraxk haup off zlog rkevhahn ifh ibrok rigijuein jtamlod. Sejl ep vobo: xctpr://jomudutaf.efzyoac.jet/gtoagixc/nididxyac/oyjuykodaiv.
Key points
In this chapter, you covered all the major areas for hardening your app. Here’s a summary of the most important points:
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.