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

ConcernBad habitBetter approach
API keys / backend URLsHardcoded string in Java sourceBuild config field, remote config, or server-side proxy
Network callsPlain http://HTTPS only, enforced via Network Security Config
Sensitive local dataPlain SharedPreferencesEncryptedSharedPreferences
Form validationTrusting client-side checks aloneClient checks for UX + full re-validation on the backend
Sensitive screensAllowing screenshots/screen recordingFLAG_SECURE on the window
Third-party librariesPinned to old, unpatched versionsRegularly bump dependency versions
Release APK/AABShipped un-obfuscatedR8 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...";
Better: keep secrets server-side behind your own backend, or at minimum pull them from a build config value that is not committed to source control. A key needed by the client at all is a key that a determined attacker can eventually extract — design the backend so leaking it does limited damage (short-lived tokens, scoped permissions).

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();
Explanation: the API surface is identical to regular SharedPreferences — same edit(), putString(), apply() — but both keys and values are encrypted on disk using a key managed by the Android Keystore system.

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.

Treat every request that reaches your backend as untrusted, regardless of what the mobile client claims to have already validated.

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);
}
Set this flag before setContentView(), and only on the specific screens that need it — it also blocks legitimate screen recording, so avoid applying it app-wide.

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.

Security is a checklist you revisit every release, not a box you tick once. Next, see exactly how R8 shrinks and obfuscates a RecipeBox release build in ProGuard / R8.