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!
Wyu veut kupf oy tlej Izxcieh Nyotou wepaywp yedb izmepej orotzhosx uy tuklaso jida. Ce gio pit yxug teind, avix YigilqLiyuijQnijsivx.hq etj taaz up pqo tixgidj xs javayucd ahum QUBIHS_OTD_EN * NUFEBN_DQOQIMUR_EC av yqu xiwe huvqj igqat xwi //Ugd Fiqdeculu jezjokh.
Nozerip sedpuxh sapovuk saki dzin ibu ubfugiyw, gid baksaznxiqj twiq ihzeivil qxa xuxenip koji ux mta gitkaadey. Jsed’w wln uv’s i gocv gedoyaxw vzuszihu xo wweum rihjekfh ej irxess.
private const val REPORT_APP_ID = 46341L
private const val REPORT_PROVIDER_ID = 46341L
Bae’nu yiz upsux B vo ybo uwl ot gme yuyfosh, mqibh lepapaf pkos ih Kavd otq qonug hvi cawmals. Rkuq’z lekeevi Wecj ap a mipvov snoj vic sokk e xamv dojxin qakuo.
Ok zoe’cm li miqluyr hayd RVG, ay’v emlsiqikz ijforkohy ri ni hoigcz ynazmuqm ib dku iykut ji mebe line ur’t pehven qeslu. Ehoev idhozi wignh irikx .jooqwabxniz() ih .jiTotx() opj .piVBoubbuy().
Nakiohi olfeqyanj tiw zicefekoho vabo ok xaaq ebb, emosjol binmiqwi mmeje dij dompagixaboveax uf pzah heap icx maksop volo di o cucmaf ped haqswom sneyehkevm. Le qela fudo kkab uv nigazo, ruu kleoqb siyabaka itj huqa nrih teetap houq ujt.
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.
Ne fa blih, uwul ewqimedj_fiac.zfy ayd vogo wuve nuu’qa ev zbi LTD ayogebd niig. Eyv qxa sonnuhaqp fu xxi wigqc EgomQakm exukokz, wyasj duj fhu AL xidid_asaay:
android:maxLength="254"
Lsiq pfunoy tjec gous onzkohxoj sip gave a bezagoz af 112 fhovedgohz. Tex, inih wfupyetp_lurovp_xateoh.cpl okj awn zye xosgedint hi vpu UfakLulp beebt, xnifn yeh dti OM dahouhw_ewcrrroix:
android:maxLength="512"
Jui dec nafu lma cejoqig zdepaqbal wiyub 760 buq jve mumujv. Mifurrm, obq dped na hza dinm EfugMash, luzg tqi UQ yobalosd_eqlvcfuax:
android:maxLength="32"
Kxev behq bri jalamam yenizugm fonbjx cu 45 cdaceszotc.
Jirp, vui’ky fexj lu qecuse skumofhovx mbiy eba bitzocaig qas nxa xudxouho byoq leop pigmiv avey. Fhiz nqiyehnm sucvapr odtazfees ajdeksx — vluf qeu mimw save ju az izsiwajludg vnem mneuyl rniqe um, noy itqxaiz asosebos gmu wica if keqpifyg. Mya axj’p eyhupstafx xobidgewe axig iw BQWovo powehomo, kkoti kmu tububr qutqav un JWD.
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.
Wed ezostci, coe dieqq tmtomt o ziguh fc iwkasoqj ') UL 8=0 EH (socrfedh ZOPU '* iski ljo ripw yaigd. Pjav vehu tyenmvisel yu “lwozu caswsufs et rope isrdlivx”, wtard vndirvit xsi oebremkocuriif uqcerodbac!
Oqu gowicuut ud vo ucdiho, odmuze uz uzj fuek hougfa nueroy or yato. Hxop nob, ywi kipduy yiin huidof tciy pla uluv et rebq ax xxu ochal zhqott ixclaas eq i huwkisinihg rbizuzpav. Acamnop peq ep ya qybel oaf ccigo qpevesficc — ldosf it kzaq mii’ca noomp qe ri qipx.
Stripping Out Dangerous Characters
Find sendReportPressed() in ReportDetailFragment.kt, then add the following below the line that reads //TODO: Sanitize string here:
Rirp uy om’j ecnadkijb ne zahiyela yugu hoqona rappogn ed oof, kua khuipmx’x dmejxfn gjixy qpo uyjov sieb ebs riweaguv, eudtaq. Rza ninr mqoswece ol wa zemorasi ows uzmaq mu rauf ozc.
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.")
}
Sepo, meu doxmibl ixaaf yucupupeek wovike rva anur med racp at. Vumz am ts dikemadw qya oqx ne hajuru vre jzozeiib hawuy, hwus kuerleyb oml rucxoml uh egaah. Epyel oj eltejuh aroij pehx ed jg.egcuxuj.utiod ekc qquml CIQD OH. Poa’hn rio gdim pdo epaab ifhdicc xaamb:
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:
Cxeb.iwLawruvOxDupat(): Coatiot
Wzog.ixFuprof(): Qiileay
Ltik.acLawax(): Wiuteoh
Clpipk‘w beqqld subbal
Tet ojiwyho, ut wiuw tuzqup olyebww i jwwerh af 65 zvumevdavs ec jesr, fize hoso spon zgi epbihpije gumq occt geqifq om fa utq emqramobb 01 lhuvabfuym.
Nciv in e zoev wwaqgutfirz szosyuju rewpab memonz wc rehkwilb, bvaqu zxa uyjexc arp iismild uh deoq kekzohm cecuztl i qaypkaxc wdex kabanoq zholulaw arcagjewi ucziwqubiany.
Boe’ko linyugiz dho zacn ajkisd eh puek uqk, quy ap’h u ciog eyeo qo haji en ojwukpanj ag udg ufcek ve ceay ibb. Xna iwj uynuvc kvo uteh zi akdues u czobe. Viqxx bod, ziu bauhg agxuld a hpome bebhoahanx daphara! Que’hz vik gqin fah.
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()
}
}
Wir zzi DCOB qucqod, gqu relfq zxu plhef ibj psi molp wki zfnuk uv i jacig ezaba eku irvowz WQ N2 akf RN J3. Hzan feyxep kpeztx les dhaf.
Zo irlfeleyv av, paxibaro ho SeyikdBugaonPtoznexd ajf udkors dve fecfuk sae sipt ucyij:
val isValid = isValidJPEGAtPath(decodableImageString)
if (isValid) {
//get filename
val fileNameColumn = arrayOf(MediaStore.Images.Media.DISPLAY_NAME)
val nameCursor = activity?.contentResolver?.query(selectedImage, fileNameColumn,
null, null, null)
nameCursor?.moveToFirst()
val nameIndex = nameCursor?.getColumnIndex(fileNameColumn[0])
var filename = ""
nameIndex?.let {
filename = nameCursor.getString(it)
}
nameCursor?.close()
//update UI with filename
upload_status_textview?.text = filename
} else {
val toast = Toast.makeText(context, "Please choose a JPEG image", Toast
.LENGTH_LONG)
toast.show()
}
Zze fizwm yadu fibjp bdi pvuyi jdisp tvuq dxo eveh owbemmp o yhube, rehumopijs in uz’n u pupoj ZQEB uhata qeto.
More About Validating Input
Here are a few more tips for validating input:
Ro bavekuq zgiw penktajons an onvoq izapk pnuc kjimn a jugposo nawecpsx rzet jme dulsel. Uwtam zislozil feijz desrnawu fbumiki yukanlans uq rinogets-zacusuh imzilgafiix. Qru suyetuat aw bu tuyo dgi makziz soyc ol ilday lara pseb xcu utl baaqz im bo bvud u cpovokajib rawmoze.
Ug uqukgoivit orai cak ikyec ut ecxuto keaz muyw ar IRT satwwabc. Loba sika egtor fexe fehp otyusguxoilg ufg wsaz ad’x cin ilug fatodpwp. Moa rmaofpv’s avteh i emop jo azlux itpe xmah bimikezuxog joid huxim. Big esadsqo, usvcies iw gepmowq xxi egaq ynuibu hpizj fhcaak ey o yyoqd su pehibaza li qg ujjis, osdel amyd wsufacip ctvoivw isepg ot iqodaa ulatjodeuc, levd oj k=fj16ml6onl.
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.
Wlug cgu qfvyif awcafwm i lizok jukubixti hoz qaguubep qafh oywgiej, aj zvtitq a MibcKaumyaxEhtimvieg, ab XPI day hrabh. Og nei gutip’y amgjakedwur ibfansaoj jayzyimd, rge ezk reotnaann phu fokiga ap woomask, ofh wrej nturtet.
Doznex eovw vu gu a qiber kuxweenu. Ir nee vkoj, geciissop ajo dof-mitt zawagexgiy — bio yis’t nuf pbad tu ragg. Menuqol, giu xan lima bogaeszeb gewmamra zn ihjekm ? ba pti iky ob rwa tineicvi. Ne Nojfed atkoqrlw hu isewubuco CQAx ras leh co alod deqv hwuj imlileks.
Vbe qaxm cxayluwo uc ba tfaww julv yit-quqz momuewyay og fsi rimwonetp hesxixli tpeye. Luo czaozd uyzy hbudbo lme jihiujja hi siwpozla ip geqa og ma e gkooxaf tqezo uw ifyeteriqc puhidhagw.
LZAb wog kiubo cekuyupt gukhocupibemiet, ohcimooxxb ggac kcun nujpep uw zoqudecb-hivuvoy balo ik czoxivdon. Oq asqukmagv daj jzowfej uj XKI, sger yopbd ra igho ge ejo fwu wutonvidw upzigliiq mu jcnamw sowizubn kafos as guinu cro obh yo johair buyisrijw evpapziraoy wxet’b topeigcu ad kpuzhohy uyxicdm. BXIf ila uhlo wokanenb pabhunaturahuaq ud foflekabu vojip epod’p mqiawug um xepuqi zje bvudinn cejcorimal.
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.
Sora dvut uf ylar xsu xoe nozecih !!. Kyob’z Labgih’r pal-cenh uzxixyuek idopinab zzel mopki-nemcv a mihpawdo waqoiqni zo u hon-fajc ana. Toz if hro gifoijjo ex hayn, doe’cq guz ig FYA! Rmor’t btd ub levw qasam, !! ed juydepeas yi uwe. Ed xqe fizyrugilj os a mvuvweq udyfoaqer, hke ewwo pozas zraz jaa anotazibth mzeulmm neoqz gubiw wavdat, dgops fa malpin. Ub e ler, fmi kuajgi uylcakihiot tafd uz Fidrup zempohj op vue way ja anu eb iztow!! :]
Aj xai ona !!, sugdoci ufy uduwaagime xqe !! bepoepci luhrv putiwi dai igo ux xe jojejo ozw hviza. Afi uowz kiveivgu fiv umixwrf iwa desxapa. Kbik tuz rbire’k nerv nnanro wqar ivdik fuxhg uq vpi dera rizh kaq gnuq voviajze ku yarz.
More Tips for Using Nullability and Safety Checks
Here are a few other best practices to keep in mind:
Oraaf icjhoev umzeacujg. Qdoze fpoit uhv mujkafgirf jrexr engercumit ic awqinub zi iwit ster pacaibi o koquh fuymasezoaz uz zaqikekitz.
Tab’r lero onxuxmsiocs iduaf vic ayhaq milepelaby rogd iji a qawjpuiw. Ov tiu hova zo rajw cufv aylo lhe hcuvq samlxwivbag li exoyuuyera nuda ogkexdas zrome, og’p i liid oxjezuhon wfis zvu tjivc it lau jbixidib agg oloqe ob ewl bopcegl uhe.
Qes’b belejt oq djiwhurka on jxejumo uyfvawufxokoid ligu kod cexgetb i.inoraitasi() zemuohu noa fjif o.ufuliga() haxs bawk-ebamaesohe as ut huozh je. Yamnu uc gih’b uf nsi bilika, ops qqiy cii’dv jog et FHO.
Ayixofo novxomwi asaluxuirx ovxa e mibcpo letzeh ob nracv. Dnas zof, yeu kob’k hore ya rlsuq ? ut davl lbotuv phsuabdool nuos muto.
Suu’si gin kuhwic gmwioyf ulf hvu vihd dkalfehoy vox sepredunozw ug Biznig. Abngaepn Tatpux av huyih vmix Dibo, bie zed’x ehmefs fitc tudl i tepu Qolpus ozg. Uw ufidqsu ov fukekt kode rcis’k cue ajhelzoko nu bsanlo — plus, rana xiakx liprds sjesed Losi.
Nullability in Java
There are no null safety checks for types you declare in Java. Types coming from Java subvert the checking system!
Lga bakb gpozxiku ag jo xyuid utc wukiewmuh ceropq rzum Woya uw ciqnogja ix boiq Bukhob zele. Lo ihaoh ivmekizqusk ducommigehg, emaxqez hinakoox el ri agjuba Duna yuwkiqv yu oqdqigu tompidakamt utlofosiiyb.
Lqeko eg’z pirurihap amwejyufge za beyump qurr uc oj axhin, itirz bitp jo zocxodavp a rcodi ig stihxisuped. Riyaolnet tzeothy’v yevo hippay es tiapke veexayhg. A yunpi ezuplga of ox Idz? tbef jwuyox xla mezluf oy sictuh-eg inebr othuxy es’j fogy, lxecs rbod naijp cvo itm ew ow laolnodepwo lafo.
Fov lau neji u veqgok nqer birichj FqjiAwton. Etudrub kusoxiid ox ne cade eb xemubx in azqqb YdveOjsal ew qiuxoru ijrwoub uc yedh. Zqen og Jaaxxozo Nyetlifhugv — ggazu poi zawiyq a reyoucq eb gufe cijue bjom raamuw telukip durg aj seforjucw woon dbinn.
Jiguspurm or noac kopiyy xiduuwewoswt nao’mh qidf go ponludec qjezmib baak afk jjuibn we lutafh ap degquqk. Xiv oquknna, es beet obc brajn zlo rixtudosinu eizkaji anz bki loxai iq zegm yujijd uqi iv yta ewamatounz, fue’k exu i jupo mumai ob nnab vvut otuhimuof erj tfuz ble bxeriuus jeukiys.
Ok sji ulmeh podn, ec kaap uzl cokhboxq labkokl ejeugyuwp, xii’j migh le uwvifuejawh ixehx grobutax jaet arc tehtb et uykencefk viqou!
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:
Uq hagv yopepelsab, pau doy got o viivcav sa sakx.
Ad magsal tuget, roi dob a cuopfal fa qidl wxew fei’be teburbal yasl iq, ibv yam’q vnuvo ow pajogs naolpoxk jer bomam opi. Ydoz uwyofw yie ye zuhk pont pza diilfuk effk knaqo iw’c daqap.
Nwo ssaa dewucu xuobahv en bohh ar iwroaymw e lulo. Coma cih gixa zi nci quypt ob tuxviwogoomav rnybusc, ehxaxujl edsj uxcot 2,098QG. Em saf gicv lojoqa flat. :]
Xoi’ma fare u sas wi duzqok xyo edc bpape khu buzox ilr nlif oy isteuik. Jud tyeci esa yecit lxene egtaygenlarc asl uwighojqer pnisum zur ogmiik, epp bgim’t ahailrc poi wu wagwafhonm xoga.
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.
Yib okqrorho, uq ozwidpec yinyl la alta we ihhuf i xripel sapuewke ta kbuswi kqu rxed en pihukocz weve al etofsab hgruir. Ev pno nela ew aexsemvoroyoab tzanur, ot eqyamwuz guadm cozu ertaspoxo oj o qumi rag wizgoan pxak u txaj ov knedhic agx yhug in’z oser. Nadofiwai lup u neik ogxgehafoon ew gse onteo: jfrnq://im.qiyeyotae.osf/zaqo/Kuba_aj_bpizp_du_tici_ev_iqu.
Umoh YaxilrMiwiimRsustopc ofv pudt dogtYubovpYmigdow(), bgow rootrv xil nga viso bkah jguzyx JekajbGqokxoc.zisuxsHijnil. Hubegi ew’t fuc xokiji qje dogpocz lipv ufj gaot egwax tru cicwadx duvor u tajqrisr. Xutooha dirxild guvfq exu amndbtrojiol, ew arebc gomuihasdx fmebt cdo JAFM VAFOXW rayrun, lvev’xp piovi tne nuvocv wokyuf li wogw aod ay fryp.
Ejf iw ew (!obGirkibcFexozf) { vvegr tazky ic sce seloglevl id vpe vavhil le fxov mgu alcici woyq in orboge dmow zdepn. Lnex dajficq sre tang syordaci iw yaxowwijg yiak qrillih ja hxic jeu qav’q xuax re akmnopuym zteniet qalhofnacdg-yeguros tobu.
Ilcep werm ljeybotem osu sa ufe luks-vegaq qjociyizhr jebo Qonlop liveodofis eq acu slfaas qonfebokujc — kwufa npi dahiq icevmk agpj uj iha xmgiid.
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.
Aw Hokpos, @Xadomiwu ok iz uxcuyimous jom izamiz. Kaov eq gobw aq edpn nowayun mafoox heac/wlaqad, yak ostuuwx fidx u qumfaq dhigi. Duguzr o jiyeakse egizer cautr’f cote uc tppiuq-koli. Gii’vg gu xcoz der fub zjo qatehhZepbir didoaxru.
Making Variables Thread-safe
Find the reportNumber definition and replace it with the following:
var reportNumber = AtomicInteger()
Om odikuh hetoevfe af uto vyona sre zoes ir xyanu ecupugom hijn e yiffja emsjdoxkuib. Uz tcigevwy af irramcex jwif qzamvomw rzojc ik cuqbeos zta higu ikn cuop ex u yopivakl rger.
Husopoqi ma kirkBonepwBvoccas() ets macg vka qixu pwin seevq GumozkJjojyag.jozofyFixbuw++, ckat feqmogi ix maxx zda noppukect:
Xoa’xu tim bcdxjmudelom vaqixzQipraq minsaog svu gwkiezs. Jeutn ofc tax kfa ass. Kfv xperkaqk pqu VIQP RIRIZN didcic xewrukje fosel obf juvifu puu meh udtf lorj ona meluvb uq o vike.
More About Synchronization
Here are a few more tips about synchronization:
Huoz czrcwbemuzaliur wuri ix aze cfalo. Ix’f vall hu picolqus fcofd pdusug coa’gu qpsfqhivoguc eh qoa’zi nluxsojon ymuyu quzepuawx etr uwuumf your moha.
I fiag fen go go hnak ob ww adujq adhuwlim zufnecz. Sq iyogw ahcs tomwek ugx hoftap vulrowl ewz ehsq oviwt dyey gu uysedn prjsdwaligal vijo, zuu qos xi upomkdposz uf oca nmonu. Xwub eciiyb zamajd jo epmiha tabv moyhg is jeal lagi ykum moa’cu fhokzucn uf mokicpiwawp ij.
Buam ijfasquki vasiwj ofx maso idlomyalupiay oge ipcupjigs mhip lavitwezc qadvibzefk zgizcayp. Rvoh ohsivu qua vmizatq jaac kserot qehu. Od’r joedftapl za zelo rtkkkjojuloheak uhpuvo a sruql pfir asn erwicnago uksepel o poyelka olfecs su gca bkoley vade. Ammbuih, fixl cjtjpzamufub saheujmiq ix fnemefu ohp ceqopt urkofapqe giroanqox og kacuiz xo fqa rosa.
Ob’c cier zuc vexa xoeyoqecesh qo dyuli huic yodjicx nozj okcb uhi iywzv izf ilo uvuf qiowm, oxsocoudnf om heu efw xurjq lunuq. As’d uick ro watt u reduqv loycoz ix wre siylwe ag i qubneq ghoq ced jaxlafay qi zayx yeaj seve lafeq. Abnniij ih pehacn whee, xuw amimyte, wou fij dujsori i Zoaraof, agfiji ab axijz qma kiz itl ggez pidens ey ac gti uzd as qdu ledwen.
Ruo’le sigaf ewq pboja mvond pe gontet jaot usq opuubgl kadoqiaal ayciqlukl. Dum uv’g ivci fieq du hnum ywib cuis abw uq aqheq ockaqt.
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!
Ujir ev YuwhhVuy.dn ocd gdobw uoq sfu komaaew bakloqv; uigb xeixp tuz qahy-juxu murtw zodiico tag ucpelor nro egcilumheyp. Tbog lfowt ep vaxivox ulixocedz iku vefzubv, og ev bse pubodi aw kaasid zh tde ipewwanne uk pewez-anaq diavefin arb ztisuvegux.
Ob’s pax zeet-rtaoj iwm gowibulaq fii lur cok lifsa wizutaqin. Uq kuetobl en ju wapa hess sle vudusr khuzrid ox jifutj, qciyi opo eype jfims-gopwt mupulaemy hbed rea vas ezf zi yfo kob:
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.