Security Best Practices (Java)
Signing an APK proves who built it, but it says nothing about how carefully that app was written. This page is a reference roundup — a checklist of habits that keep a real app like RecipeBox from leaking user data or being an easy target for reverse engineers. None of these are exotic; they are things worth reviewing before every release.
Quick Reference Table
| Concern | Bad habit | Better approach |
|---|---|---|
| API keys / backend URLs | Hardcoded string in Java source | Build config field, remote config, or server-side proxy |
| Network calls | Plain http:// | HTTPS only, enforced via Network Security Config |
| Sensitive local data | Plain SharedPreferences | EncryptedSharedPreferences |
| Form validation | Trusting client-side checks alone | Client checks for UX + full re-validation on the backend |
| Sensitive screens | Allowing screenshots/screen recording | FLAG_SECURE on the window |
| Third-party libraries | Pinned to old, unpatched versions | Regularly bump dependency versions |
| Release APK/AAB | Shipped un-obfuscated | R8 shrinking + obfuscation enabled |
Never Hardcode Secrets
A Java string literal compiled into an APK is not a secret — decompiling an APK back into readable-ish Java takes minutes with free tools. If RecipeBox ever talks to a paid recipe image API or an analytics backend, that key does not belong inline in an Activity:
// Avoid: anyone who decompiles the APK reads this instantly
private static final String API_KEY = "sk_live_51J9x...";
HTTPS Is the Baseline, Not an Option
Every request RecipeBox makes to sync recipes or fetch images should travel over https://. Plain HTTP lets anyone on the same network read or modify traffic in transit. The HTTPS & Network Security Config lesson covers locking this down at the manifest level so cleartext traffic is rejected by the OS itself, not just by convention.
EncryptedSharedPreferences for Sensitive Data
The SharedPreferences lesson shows the basic key-value API, but plain SharedPreferences writes an unencrypted XML file to internal storage — readable on a rooted device or through a backup extraction. For anything sensitive (an auth token, a user's saved account info), use the Jetpack Security library's EncryptedSharedPreferences instead:
MasterKey masterKey = new MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();
SharedPreferences securePrefs = EncryptedSharedPreferences.create(
context,
"recipebox_secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
securePrefs.edit().putString("auth_token", token).apply();
Client-Side Validation Is a UX Nicety, Not Security
Checking that a recipe's prepMinutes field is a positive number before enabling the Save button is good UX — it gives instant feedback. It is not security. Anyone can bypass your app entirely and call your API directly with a crafted request. Every check performed in the RecipeBox Java code must be repeated, in full, on the server that actually persists the data.
FLAG_SECURE for Sensitive Screens
If RecipeBox ever showed something sensitive — say, a screen displaying a user's saved payment method for a premium subscription — that Activity's window should block screenshots and hide its content from the recent-apps app switcher preview:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
);
setContentView(R.layout.activity_payment_info);
}
Keep Dependencies Updated
Libraries like OkHttp, Gson, or Glide occasionally patch real security issues. Pinning RecipeBox's build.gradle to a two-year-old version means missing those fixes. Review and bump dependency versions periodically, and watch for deprecation or security advisories from the libraries you depend on.
Obfuscation as an Extra Layer
None of the practices above stop someone from decompiling your release APK and reading (messy, but readable) Java back out of it. Enabling R8 shrinking and obfuscation renames classes and methods to short meaningless names and strips unused code, making that process significantly harder — covered in full next in ProGuard / R8.