App hardening: root detection, Play Integrity, R8, FLAG_SECURE
Hardening is layered defense for running in a hostile environment (root, emulators, hooking frameworks):
- Play Integrity API — Google's attestation service: a server-verifiable verdict on device integrity (
MEETS_DEVICE_INTEGRITY), app authenticity (official APK, installed from Play) and licensing. Unlike local root checks, the verdict arrives signed from Google's servers and is hard to fake in-app. - Root/tamper detection — local checks (su binaries, Magisk traces, the debuggable flag, emulator markers): quickly bypassed, but they raise attack cost. The response policy is bank-specific: full block / restricted mode / a server signal.
- R8/ProGuard — minification + obfuscation: smaller code, meaningless names — reverse engineering gets harder (never impossible). Keep
mapping.txtfor crash deobfuscation. - FLAG_SECURE — bans screenshots and screen recording; hides content in recents. Standard on banking screens.
- Debug protections —
android:debuggablefalse in release (the default), debugger-attach checks.
// Həssas ekranlarda screenshot/recording qadağası
class TransactionActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
}
}
// build.gradle.kts — release hardening
android {
buildTypes {
release {
isMinifyEnabled = true // R8: kod kiçilir + obfuscate
isShrinkResources = true // istifadə olunmayan resurslar silinir
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}FLAG_SECURE and R8 configuration
The balance question comes: "Is fully blocking rooted devices right?" A good answer shows the trade-off: full block → simple for security audits, but legitimate power users are lost and detection bypasses become an arms race; risk-based → viewing works on detected root while money movement is restricted/step-upped, with a signal to the server risk score. The decision belongs to the risk committee, not engineering alone — saying so shows you understand the fintech context.
🛠 Practice task
Harden a release build and verify the result:
- Build a release APK with
isMinifyEnabled = true, confirmmapping.txtappears; open the APK in a decompiler and see the renamed symbols. - Add
FLAG_SECUREto a sensitive screen and try taking a screenshot. - Check how the content looks in the recents screen.
- Test a reflection-based DTO in release without keep rules — reproduce the "works in debug, breaks in release" case.
Done when: you have filled in the layered-defense table for your own app.
📚 Sources and documentation
- Play Integrity APIofficialdeveloper.android.com
- Shrink, obfuscate and optimize (R8)officialdeveloper.android.com
- OWASP MASVSofficialmas.owasp.org
The industry standard for mobile app security — bank audits reference it.