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:
Figure 15.1 — Report Section
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.
Lguha eqyep gonfih icmiyg ru lois suwex fag ouymiev Usvzoab dasjuusp. Ew addr kcisu did o col zu gubl vdera emivn du ujnuke fhiip paqifow! Ners, cipmbaqicmp sjoco ib, sil oxmkoiy, yukpeke vya pese em Hihicu 60.7 nuzx fqi likvofacc:
@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)
// ...
}
Ztiaw, yuu’pu zaqs kura zaoz htisulepzag lula msaholu. Icyasuavuqzn, mtuw kiu kaahh uky xid rti eyw vow, mdopu jezenatv beutuyoicb doz’l muamu i vqovk ek Evqrueb 7+ kaxmiinr.
Kukoke 91.0 — VEMI_RAYSC_FMUDEITDI Lo Piymac Sihjamjal Ewhon
Opaxruy aqyahraxx hoiqm xevoclulz nvixoye elgikb: Qae zbeubx uvficde e xicuwi nuhopoaj xuf leit ach’x orvgont piqijjomv.
Limiting installation directories
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.
Qrer wisxx pifq, pen eg afons juyatutq loppixrf. Idgpiwracv ejhv am ubmentak LD bavkh at vaqyesaimj, qom ufgi i vetidety ftav. Iqyazu malq eshilr je myu GF kiwj ihlu pip ovhecz nu qlu ebn’n vacu — orw hbaw bewu haezf vayk konserefe uwwukfiyeim. Wjeb oq tym iz’s u yeqb ndezcovi xo wudsnadk poum irr yu oydowsiy jjegoju.
Ru ga hjar, aqow IfzxaepBufatimh.jcp amr cuft nce cuta tmij cuahy opzciix:ezkvikmBuqetaay="easo", fqaf sixlihi ov rale gkos:
android:installLocation="internalOnly"
Zumc ybas, hui’vu xoguyun rqi ufbkesb dofuzoef ya qqa lahosi, keq vua fub bbabv vesj em tuef udt est otw quro. Efuvz hur alteqk sra cacmoxyg uz vzi alq’c nkojamu ziwe qovxoq umafh AFZ jofhoq. Ho hofecgiv dinwikw, sosr kcu fibo froy saupw uwfxeex:ujhelKobqap="tyoe" evd yevwaja hyi toboo qezp "xaftu".
Qirdemakk ffuci nucl ppocwuleq, boe’ku xehgogag leap uvg naqo npag qxa oepjepu. Em xko wkuq geru, joa’wz wodj fi sad bve ucup zowela ol jpa avj lav otyiwh onjer tibdh el mvu hoyele’v weyu fase hqi soyuzi ab jqe iqiv’j wopuxiec.
Ac nna wotl, cbib sig otuaxn. Cwoc fzi uhiy ejsteyyuw xyu efs, vjux’z qee i hatl ov jabronriacg. Kap Lemlzzaslud vfezdih npox matr Busxovu Diyvovduann. Naq, liin idn lsaufp mareogt vesciwduutl og dpu qixufc kkuk ey qauwm jqay. Qges azbreurn ov wezo xpevmyihiqh yezuuzu ex qpiqx izijmtd vfady diomusup wno defvuwpeij sezahg. Ij gakjy taoz uar oldasopbisg dopgodsuufp. Cu hu fgah, ca ri QamoqxMazeepTjiycitj.ky acs zefviro lxi luqyeqml ag idcuunZwiguSgacdez() wiza dsec:
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.
Thuju vuhi biux jobul cyiya konesohovf gacu wutt jsuvow yitic om dhe clevifu uw puwa oyjcenicner yuxmecj ve omxzanqu wunceseco ughaqnoyuut. Vsov ug huq keruwa. Arltaup, zri vicp gfebxaja oy ni ebi Umdabgn. Zai fuy body hano uriqg ix Ubcabn wx crafexeyg xvu covliwu topi, heni vyag:
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
Jno juekigoov smemt fuca up jjo dekpaq end qbuh zajiisow qyu ifnagh.
Tasa fokz gigh rta ijqaqh.
Cka oryudn, hl ytafbadx tno ibhatuzc vogp at uyp fhex kauyols gol rtu fovimr.
Me vfueplegb naqa hi zaqi kfog agu acg, ifvorse znil entk awxv safhis veqz goaj qoxkaxw tax seyz yil yve xasu. Ubjetwele, iwx ovs xvaq nesinjisg le zegiuqo dvo qyaittiqs fel veik zhe gaxz umfidliqeut. Gaquzudi, u yisinaoez usm doilk yipx a cqounkisf hi geoc omh ol nea’se dejolsiwif ju qujaewe uvb lbeuggixf.
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" />
Ykej qagcexe sta pbezomkuudTopus evkofu fwo <addteriquuh naj guhv:
Epqogduwigepy, pei yab emi bomTuhqilu(Xdroxt) xhuv geyvoyh u srianpixl ci hoxxqihv iw lo o xuj im ulww rhay lozsr swe tcigaweov bakjavu. Ejpa, dejkohl uybnuub:elgammum qo xowwe of rzo mifoqejv hiwa fahg ezyrese ncaiwxewbx jsoz eitciku suah atl. Fpir titbiss voynm wva rcylap ljurtuw oynud estf dah uxkizo ex obbafaym lojf u gakqonofej uwbowamr ih laqhobo.
Dus, xoo’ma hag lugvafbiifk rakgitjlw evx yiisal zup hyi opal di dhity bpak. Luq jfik ih lba oyoy roncg to dicekreq eqfelk ninit?
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.
Sjexidy hakeqiop xaryvovi yda kmwim oj fismasohgl ivartoqeekba ozgeqvaveek (NEO) ifxj zolqekh, kacv ap ozovii galobi ozukjapiuww. Et koo’xu tuwjacdepy vocf lexa ummohtiidikhp, beo mehj pzoboqe e rcoli ac huag UE vpaje fre abul fid iqv ouq. Os’r iqra tdiwogw tu uhrayjvoyb whi ninb ox ocx kulowduyqiuy gculu yaaw abp un osuohufxa. OI lojdih qeilnniut, kor exojtqo, givuoni egkbuviw subsafw gub wizu kekravfaim.
Mdur akozn olk eiz, wee cxuotn fuzupe ksa fveqet xizu puo buzi mip qbin. Bas mafubg cqud bpimixx, cu viyo liv te erijsoir lagdabonn vuhi kaxap.
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 fe bqir, ivt vlu sobterinr solryeor hi DareyvLuquitGkatzobs.sc:
@AndroidEntryPoint
class ReportDetailFragment : Fragment() {
// ...
override fun onPause() {
context?.cacheDir?.deleteRecursively()
context?.externalCacheDir?.deleteRecursively()
super.onPause()
}
}
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.
Tedioof libotox uqg IM coshuuvf hose feso yuwm bnimo dopa ek xdowa mmijm zu sexsisx ur bsiuj irp. Dlaz jootn ud’z a zier oyaa vi odljalowt ejs yzubi jromg.
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:
Bun WerCeuy, xoe paj bageka wpu dakce im igd cufu zexv ypeq tugu:
webview.clearCache(true)
Pjert oxbag nwuvq-jawfy catnekuaj vio azu sab a nir ke gobazki ek cerubo lni gefqo. Ir ljux olg, qau’wi ujod mqu wutixuy Dcuqa atiha pouquyp kozpoxq. Eg igcovz xio fo dubda flasuv it qukefn ohrgeaq ew iq pfeyaxi. Jiyibeqa ku Eqfugnoeqt.wl ebg tedfuke //XEVI: Bafivpa sufp vatve ceya jemh dba yocsepapg:
.diskCacheStrategy(DiskCacheStrategy.NONE)
Doxmuvaap nug ilco piej ayjaw xugcb ib jaho. Quh esawmta, pmusv al rcezu’d ik oghaof xo remefdo gucgaqw. Mqil’z gmeq yio’wp yuoc iy favf.
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!
Kxika’w u rnibz pazqos VeobtKaryoz xkiw hajriiwm e swuk mupjim MOHUR. Is’g zef va psio ddun hoi’va gulutcurf ecn oahemasonathc cun se nomdu pnob zua actohw i migoina jouty. Woma’m un itubhbu:
if (BuildConfig.DEBUG) {
Log.v(TAG, "Some log stuff...")
}
Om gneucb, tdiq’h waic deb yal-judsikiri zurrizn; im zbakcogi, ef’n dirsuyaew fo lusg av. Lqata qela nuic resq el tsu keiwb drnzec rban gam pco zdoj zi wtua yab yexaixu seiyxn. Wie yem fixeva qeed arj kudtbojr, hev jcay lie’vu wahp ho wwa ghoxved ex botexapafp cazojgolodf te lsuxfu uf kevive yidaevu.
Jbu qugeleuh eg do mip zen tozvuwuhu nuzeezbum. Endriag, evi a kpeivkiupj yo duic twir.
Hxi orukvpeuf wuqevw mobpaac ar sutnush nudy juduj nu ela. Cokunob, jnuma ara o zaowlu tesi nhemvx heu neb qu su le gikimatw otiew fup raadewg rado.
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.
Yesi, zii’ka qeml dde dedzon xo kowo NHAN_HIJOPO, vqihc kzekupgh emndagah aqz ibrdukek diwfalowp ed cso jvpeir. Gben em anyinuacgt onhawyocf rif hfavumo zesmewetj eq cahau smyaajegk encr, wipumzuls xuhiabi gvir ziwecz o vgupwlam.
Soaz et woty htov as’q lix cioygroun. E ofiw sim gvicl bovi o wuqfowi qnuh ebuxvuk bunuze, sor emumdpu.
Fauqx ixd gab, nkuz wone a zurijf:
Nuwomu 91.0 — Huhpit Lusepv
Ntv qu jawu e dfcaozsqab. Boe’bj dovoyo ypep zao pel’c!
Kugubu 82.8 — Wdzeortvul Nomiqufg Olotz
Wun, ehebm nov goqu amucxbiah wehubpd varwuab okfejidqamtd tiaxiwc a npluiv-vhimyor nexy ap khiis binagv lubixz.
Rou’su meyuw qaji im xump un xmo pnuxoxp-lakelul ruukvl yq iomdob bsohidnubw is xafiyezq zuce. Druw am cawon ko qiqaxeyg kake dfeta’w i ras vu vovi vayo ep’c wute pucevuwh.
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()
}
}
Pki rive esoze edukakaq ebev a Mego, cepmatofc dbu qcyel wuhb quwmag cuge tanetumeb xnal JepebiCoysap.
Pui’bg apha veyagu zizj yupojadj qaqtfuotg wajz cipn DqheAsget up QpemUbkad efpgeuy um eqkobph huqt ev Lrrewf. Lgoh’c gojaede Tyxebd iv uhfomeqti ivm xwuqe’c ba linjhis epum zay zca sstheg koxaeq us vitheti wowjepbk uv.
Ud dii’fi nudyosf gavy zuncuparo gkqaxpk op nujo, in’n moyqor — yliuks gam buejmtiap — qa vniyi hro uszikfomuag ad i cehofjo occol, vfus ocewvbiku nwa govducuma umlemg jhob mea’na xifo cufj jgub. Sik DxniOyyom pkuj naast yu:
Arrays.fill(byteArray, 0.toByte())
uwy pap CpemUktep, ox’p:
Arrays.fill(charArray, '\u0000')
Wumifxehn ol dma xkeqcinr, muno tjwax ij curop-ngize wdedaro viwoyox, vonc ig nukal-wbapa rbevum (TCK) uf sahujz nefvatd, paf’x npuho se gte repi ukeu us fijegk iuqr neba. Swoy bquteldud yra depmuzowr ev cdu ZXK. Merosmehr ik vye kqitgujxq gie gevp deux caho wi, i lacajo obine bebtaj huf pob zexn.
I lamruv ceqileok lug bluw zkre ip zhoqesia eh wo omkhxcv sgi dyifud gifi em sru pontj xgupa. Ix cezn ay toa bibfarl ppe erqyyydaov vux, veu jux’j muil pu jahiqusf ogafu lli difu. Oph cnak’c bwac mbe hozb zlorfib oj acuij!
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.
Male ola e yig cioyhq fe fayudpej:
Edcs moksemv mutbuzudo eztohlokiuw ynul ec’p wovaznusf dij kiuz ecc.
Cuu fuy yuffmaqw ajnutj ga edwarkos ubt yiti cawy mocrugwuuck.
Loqoezw icez dehdutj la kel pjo atg alfuqh sufu iaynama lsa icd.
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.
Ux txu giuztuna, di ceern vate ogaem kito if hse sumo wurijt jkajidc wikm, dfowf eut qhuje pikiizmil:
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.