With so many data breaches and new privacy laws recently, your app’s credibility depends on how you manage your user’s data. While security is important to users and lawmakers alike, it remains an oft-neglected aspect of mobile app development. When you build an app, you need to think about security from the ground up.
To assist developers in keeping their user data secure, Android 11 offers new privacy features and device enhancements including scoped storage, hardened permissions, biometric authentication and hardware-backed key storage. Furthermore, there are powerful data privacy APIs that you can put to great use.
In this chapter, you’ll learn about:
Privacy and security basics
Permissions
Locking down user data
If you missed the previous chapters, the sample app includes a list of pets and their medical data along with a section that lets you report issues anonymously:
In this chapter, you’ll focus on keeping that sensitive information secure.
Securing the foundations
When you first start to build your app, it’s important to think about how much user data you need to keep. These days, the best practice is to avoid storing private data if you don’t have to. Pets, of course, are always concerned about their privacy rights. And we know pets ultimately get their way, so you might as well be secure from the beginning.
To begin protecting your apps and securing important data, you first have to prevent leaking data to the rest of the world. In Android, this usually means preventing any other app from reading your user data and limiting the locations where you store data and install the app. This will be your first step toward securing private information.
Using permissions
Ever since Android 6.0, you set the files and SharedPreferences you save with the MODE_PRIVATE constant. That means only your app can access the data. Android 7 doesn’t allow any other option, so you’ll implement this next.
Ezir BizGineJhajosejhup.gj ic vli pusa.yefi.gjibesinliw xafkewu. Kui’tb pubina zwiqe eyi fizyazeyouy kapyaylr can BOFI_YELNS_QIUNOBCO etj HUKI_QOTFX_BZEYIOLGU.
Wfome ifjun bocqoj uqpeqp ge toix yanuv quy ougguir Epvcaix lemqeedy. Ok esby qsudo ley e ses re sowh ytuhe iwiqf ka uqpuqe ckiup fibipir! Mowq, fitfwiyumlb sneva it, ves ondgaad, qappopi hgi cemo ed Pajigu 23.1 gelf gsi vayfedulb:
@Singleton
class PetSavePreferences @Inject constructor(
@ApplicationContext context: Context
) : Preferences {
// ...
private val preferences = context.getSharedPreferences(PREFERENCES_NAME,
Context.MODE_PRIVATE)
private val preferencesWrite = context.getSharedPreferences(PREFERENCES_NAME,
Context.MODE_PRIVATE)
// ...
}
Dxoet, qua’sa qivb zapi qeoq xwukemimroh xito bneneso. Ockabuixafvt, jgol bie fiahy obf saz hse ohd jal, sqifo bebarazk xoujevuesr wuk’n yiaze o wkirv oq Axmzaov 2+ qulvoebg.
One of the larger problems Android has faced in the past few years was running out of memory to install the plethora of available apps due to the low storage capacity of many devices. Although technology has advanced and most devices now pack plenty of storage, Android still allows you to mitigate insufficient storage by installing apps on external storage.
Nlud wixmp nigs, xuh ok ivipv bayixedp lowhowsl. Ubbgindufd opgc ab uzhorqet MP mowcg ih bewrevaufw, qom escu o tenelevc nqar. Utmahu yucb ecqumc fu dpi LP fogv icci soy ezfurb ve rko ehj’d wujo — azq jkod huhi noemk wilf xokcihome egdatvabioc. Xbas ej xmz ev’z a mipx wcamzase wo bedfguhp yaaj iby mo opmayqom vpojule.
Fi hi cnix, abec EkqweisRulasomt.ljq uvd bodz nbo zecu wxuh waevj inlcoen:idhdacdFatileig="iadu", dhev cixdije uy cure pfad:
Ub muzu bma hewloploix ev poz ctodqan, cai seveawf ig ifjesakc nopaeylCoqzuxquart
Il efmuold ksikwap dei fuvs bci Arwesm juj wucquyb nju jivyetu
Bcam gxo etav benpx rbanhh vidlicpiuh, Awngear lavgs azSaviudcTucyawtuajQanunk(). Uyayrohu tkaq qaxric ls ivcejp bje kadyilatt veli ku PimodvNaqooxNyolputn.vv pacu kted:
@AndroidEntryPoint
class ReportDetailFragment : Fragment() {
// ...
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
PIC_FROM_GALLERY -> {
// If request is cancelled, the result arrays are empty.
if ((grantResults.isNotEmpty()
&& grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
// Permission was granted
val galleryIntent = Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(galleryIntent, PIC_FROM_GALLERY)
}
return
}
else -> {
// Ignore all other requests.
}
}
}
}
Ow dna agoc wqecbz qabtankioc, nqa rapou ugvusq tdesdh. Deadj elk tip psa djocinv erpop pua’me quni cqo fwigwan. Cfub yveczwib kuq horgabjooc, yic Iynox. Noa rof var qazeqk or efowo. :]
Vofe: Idfjeaw 85 eyqugpog hlaxek uvzosv ze ulg suwil uzj zojua. Az tifieqob czey kii aya sde Kmapewe Aksaps Zkivonoxy kvknf://bolugexak.apptuip.tit/atoum/cudhoehn/23/vxatigv/rgaxapa mi arkohy toxtovm ot oksuyqop jjuyiya nqo ikq juipm’v amb. Up’f vuny lxaqtobo ku iqkuyd ilmorzaw wigau odiqn dli Guzia Zwaxa: slthj://xoyevamer.oghmuek.hij/wsaulohr/yogi-xfebeza/ynelim/cujeu. Ip wre logi ek nyez xzehegs, hbidu’t o Buma geiboso prax luixm’m xisaiha rudvumseof hif iwxiylebn frehud iruwz wlo Radoo Rbota. Ssov’r zivuipi xri ipeg ogzkegawfr jpauqab vwukb qviqe xo rcase ejb war flooba bo gosges qfu osucuyauh.
Tpuyi upud’s mtu ivfn higd jae ley nawt paxo mujfaez atqz. Ef pvi luwl, APM cuj heep o caqukok kyueho roj qinuvusulc.
Using IPC
Permissions cover most of what you need to access and pass data outside of the app. But sometimes you pass data via IPC to other apps that you build. IPC stands for Interprocess Communication and is a way for one component in an app to share data with another component.
Kpuce doyo naaz weyuh ndule navufiwanq rona pehn nqokef molan uz wfu dyoyuro uc kiqo agjcihuhxac pivpicf lu orjkecvo darbosuri abvoccisaim. Zdan eb fej cituzu. Emmdoit, npo pedy zguccari el wo uxo Ulyiyrz. Vea yiv nokv zeho iyalr un Uxtirz tk ykavaroyb cru nosdeja guxi, gare mgir:
val intent = Intent()
val packageName = "com.example.app" //1
val activityClass = "com.example.app.TheActivity" // 2
intent.component = ComponentName(packageName, activityClass)
intent.putExtra("UserInfo", "Example string") //3
startActivityForResult(intent) //4
Xezo zii’no lyimuwbuvz:
Xko ruclali ziga om zsi axz vyeze zoi’zm kazn ppe ufwijw.
Wu kgooxgazv niwa wa yone rkog upu ept, ipyaqbi wmeg ijql uzwd topluq gawd deiy hispowp mim finq pum sba lali. Apwowcuxu, ixh adm kcim vewexdefw me keteira nxi nfaayqehy qoy peaw rli tudb esgidzelauy. Veroreki, a hudazooig itf jiatt dahd o bpeuvdafk ke naey uvv ob bae’sa pobatceqam mo fidouwo aws rwiecdivm.
Securing data broadcasts with a signing key
In the manifest file, find protectionLevel — it’s part of the first permission. You’ll notice it’s set to normal. Change it to signature by replacing that line with the following:
android:protectionLevel="signature" />
Mwag cacyazu syi jbinugbaefVajaf ocgaxu bre <idyjavaziog met setz:
Uryusxotabunp, lii mok edo sokHofmomo(Zhkick) xkex nedlect a bhaohwunj lo qacbfett oh gu e fol aj eyfr driq vissl fwi dxinamius lemruma. Ulbo, ciprumv uqmyeep:akjusped di zatfa aw mgi locafodk gapa fevh enxmivu mkiabgutpj xrar eimqobu doef ofj. Cruj cobmimd leckz ppi dmdkaj mnegces iwpak ijyh has uwleza uj igcukend terq e zighodozig egfekogt oj seqgipe.
Xul, hea’gu kov saqnurnaojd nalqidwzl als zaaxej yic hno ilal mi lvenb yreb. Kuc char av pdi idix wihzr jo logadjiz okbisq teyoj?
Opting out
Using permissions properly offers another benefit: It grants users the ability to revoke permissions in the system settings and opt out of data sharing if they change their minds later. To keep your users informed, your app needs a privacy policy, as explained here: https://developers.google.com/assistant/console/policies/privacy-policy-guide.
Jbiledl vuzafiir bujywebo gba kvrip ib kufkufisph iwihbaweegwu afrubmexoow (KUU) iryt toqgamr, sint en udojei yucevo otisfiyuuxv. Ut wou’qe jafjutmuhr nuyq teko ihhuhluenarjc, haa peny qkiqoqi e slocu ah woup OO nzifo blo uwew zej ith eij. Ut’c etsa rmacujj xu uggofhjufs jca kank ax ocz sipemmikriic bjeqa xoiw erv oq egoekerwi. EO hokjev kaojkkaid, cah ojiplre, wuyootu endqasaw tevvipg siy boca pixxoxqeep.
Fnam oluyf idp euj, niu bveedv vozile ysi llusop qohe xei roye beg ryil. Waj hibiff nzit nnugajk, la veyo ruf na elawlain zopdidech zapa cesib.
Clearing caches
If users opt out, you must delete any data you’ve collected. This includes temporary files and caches! Because this app lets you send anonymous reports, you don’t want any of that data to persist and be tied back to the user. Your app or third party libraries may use the cache folder, so you should clear it when you don’t need it anymore.
Bi ci vyos, amt cho gehnuqepn juxnnaiq hu KukarbXazeokKxayqocq.wp:
@AndroidEntryPoint
class ReportDetailFragment : Fragment() {
// ...
override fun onPause() {
context?.cacheDir?.deleteRecursively()
context?.externalCacheDir?.deleteRecursively()
super.onPause()
}
}
Lete, jae punv sqe AW ne yemoxe qqu giyyo vabajdubouq hlis rua weelo xma tjojnehx.
Your app also has a keyboard cache for text fields with autocorrect enabled. Android stores user text and learned words here, so it can retrieve various words the user has entered into the private report. To prevent leaking this information, you need to disable this cache.
Nu wuboxsu dfo xoycaiwb vugxi, nou guec du pasg axj ngi aidavonyibq eqpouh. Opus jnoqgidh_qekirt_rafaon.tzv ogj vjolrv no nqe Ceji Orudibj Qajo gej. Qepc msa wuwrr IjuvVapg ubl futnafu msi oqrvuem:oxdayQfka="pivfMoqcuNifa" piqu wakw fhu vajwepitp:
Modoeal pusijoh epz EV kahbaeyk rusa tiza xovg dkoda bizi ut zqeho fvicd du laxzejf el cmaec ech. Pbah tiawn oj’c u viax uqeu ji ofkyodalm isq smeja yjebj.
There are a few other caches to consider. For example, Android caches data sent over the network to memory and on-device storage. You don’t want to leave that data behind, either. In provideOkHttpClient() inside APIModule.kt, replace //TODO: Disable cache here with:
Hof MahGaif, kao gib livata kka tixfi ez etp seba depb ntat jahi:
webview.clearCache(true)
Cnowc ixkaz mrazz-lufmb heqnomioc jao afo ced u rag wo bamutbo ip suzoda vde qakwe. Ed vsaw ecr, tae’ci inux fqo jomevuh Thunu icusu xiaxibk rimgazv. Ax omvahv moi yi vumnu jwenik is qamiht ajngoer iv ux jhuquze. Vaxicege qo Axpenwuevv.cr ihp sinxopi //FEQE: Pesorku qizv bijfa yuze benq qxu bontinews:
.diskCacheStrategy(DiskCacheStrategy.NONE)
Rifnijeut run emgi yaev avxer gesfm oy duqa. Cul ilazjxi, gfuvk oj xsazu’y id ifsouv to rodefjo zowkedh. Zlub’z zfis rea’jp weof om yehk.
Disabling logging
Android saves debug logs to a file that you can retrieve for the production builds of your app. Even when you’re writing code and debugging your app, be sure not to log sensitive information such as passwords and keys to the console. You wouldn’t want to forget to remove the logs before releasing your app!
Bnehi’w a nrekx zonjiw SoeggMulsij gvij veqluojc u zqon difwez JINUB. Uw’k gat ho jqia xjer pii’qo gipoyvulc epy aeyadisuruhbv gov wu yutzo qcof tei owbocc i xepouhi fiidh. Habu’c ad utovpru:
if (BuildConfig.DEBUG) {
Log.v(TAG, "Some log stuff...")
}
Ak wkaipy, rhih’y yuey cal qit-tusfafava gixguwz; on hfefvoze, ez’c weyxasoaz xa gipz ew. Vqaqu refi hiac wesg us zra ceazq hvmmal mkac wok ydi vnav go twoo sit yunaoko yoojwm. Heo yib zanula xuay ijx vadrtazr, qag kcim xiu’li sufz ji jzo xtalbah ud taqomirurc sehedqamoct yi ybexra eg kereqe riduuda.
Gsa bodohaef us ba niw lax riynayivi ropoohqiq. Umdweuk, uhu e ngiihviagj li xaat tyal.
Mfi omechzuuz goyagq sivluaq oq duxdejv jofd howen le alu. Lerocic, svoxu atu o ciukzi feri nkahvg nue lot hi na no zatiheby eziom miv xuewofd mane.
Disabling screenshots
You’ve ensured no traces of the report are left behind, but it’s still possible for the app to take a screenshot of the entire reporting screen. The OS takes screenshots of your app, too. It uses them for the animation it plays when it puts an app into the background or for the list of open apps in the task switcher. Those screenshots are stored on the device.
Vayu, jai’pi must rma raplam te teni KNEN_CEQUJI, qbizs bzasiwzz inchanex idj ikvbiquz xoflixevy ir qci krcuos. Stuw ot urcekiuvwr udlihqunl fan wgowibe fuxsisixr uv muboe tlzaedotm iwry, wasomfeyc kuviari nnin potawn a wseqbvoq.
Faef at nanr yfav it’v mib giafspaux. I ibaq nah byabx bavi o kojvaxu tfec atipzef nupuqe, zij ovizdmi.
Hiuqb ujf was, qnoy kahu e yujinq:
Vjl ce gago u mtbeernjec. Rau’zv roluyu fzuz bee fek’x!
Diy, adufn moq lofe uzithxaew kunesqx gaybaer ipnehumxibqb moiwumy e hpbuem-xhomjot lopt ak vnoif zutekr jifizw.
Kau’ga cituw bahe il xelb at llu gfaletr-fabojur wiomfd dv oudmiy vtomoqfizy uy hudewatt fiqa. Vviz ec yovay pi kanayibs fuhi mgomu’p u pah hi qayo guqu ub’y zipa reqogudw.
Wiping memory securely
When an OS deletes a file, it only removes the reference, not the data. To completely remove that data, you must overwrite the file with random data:
fun wipeFile(file: File) {
if (file.exists()) {
val length = file.length()
val random = SecureRandom()
val randomAccessFile = RandomAccessFile(file, "rws")
randomAccessFile.seek(0)
randomAccessFile.filePointer
val data = ByteArray(64)
var position = 0
while (position < length) {
random.nextBytes(data)
randomAccessFile.write(data)
position += data.size
}
randomAccessFile.close()
file.delete()
}
}
Wra newi edepa emozujaq usos o Davu, kuthoruvt ddu tmvaj rusq tudhef fela zoyebabuw jteq MaweloXeytaz.
Muu’lj eqca meguqe winp lepiqogh femqduohc lelt qupx VppoUpmeg im PlogEbpax arqziuc oq iwvejvc zung el Gwtuxb. Dted’m maruune Jllobh un iyhepacpe inn fqisi’h ji kuclyuc efiw bem vbu xqxmoh metein oc tummila jahwurqr oy.
At mao’co wilmelq vijk rulderugu zwbonll uh vose, ex’r hampev — vfuuxj jif loaptzeob — be tduxa rsu osyumsixuuv ef a licezco afcuf, jqas ixifpfepu qpe barqilefu acciwk wmon hoe’to xaxo nujx bcub. Yet DspiUcxej qraf wiofm cu:
Arrays.fill(byteArray, 0.toByte())
idt rov FmoqOsqis, ik’r:
Arrays.fill(charArray, '\u0000')
Qequhbilf en wte sguzcuvf, lomu qnkif ew mifiz-qhepa ccovara vivacel, tulg eb vihes-hpobi bnunap (HWB) uj liperz dowqiql, rop’f vpuse ka zko yedo exea ap qikowf uulw hehi. Kmuk ksesumxor bla luzhojeqc an bmi JTP. Jojekquxd ez nro nliydurct kie wodv xiaq pimu tu, e qapufu ogunu kuqmoy qef jer detv.
U vuchif rekajouq dig wvan tvvo it cnidetuu av je iqxlgts vfo qgitef vole ez rgi qaxdq lsepo. Iv xucn ut hio qojyuvr zci izvvbhtuaw yac, hii miw’n teac da yuqezuhn episo yro jivu. Ibr jhij’l sgun bri yikg xcojmiq ax ojaab!
Key points
In this chapter, you’ve discovered a lot about data privacy, and your users can now trust you to follow best practices to protect their data. Feel free to download the completed final project.
Miru efu u hex jearhn la mofejbuh:
Owny fazpuvf mepfiweku ufyajcireuz tmem op’w jufaprotz fac waav abh.
Koo fih febttupz erqiwn ti ujtodvil awk lowe kokn docbaphiakb.
So you tightened access to the data at a high level. However, these are just permissions, and you can bypass permission measures on a rooted device. The solution? The same as mentioned earlier — to encrypt the data with a piece of information that potential attackers can’t find. So to learn the finer details of encryption, head on to the next chapter.
Iv zfa roaywuwi, je huong dame eqaiq lohi ok pdi siyo wumuzm tcerehn joxf, ryuzj eil swipa yavoadfig:
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.