BookNest's new lab button stores one stand-in session token twice, once the tempting way and once properly (Secure Preferences):
export async function secrets() {
const token = 'bn_live_7f3a9c21'; // stand-in for a real session token
await Preferences.set({ key: 'authToken', value: token }); // plain XML: never do this
await SecureStorage.set('authToken', token); // AES-GCM, key held by the Android Keystore
return show('secrets', { readBack: await SecureStorage.get('authToken') });
}Preferences wrote exactly what it was given, as Preferences showed for the cart:
adb -s emulator-5558 shell run-as com.example.booknest cat shared_prefs/CapacitorStorage.xml<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="authToken">bn_live_7f3a9c21</string>
</map>The app sandbox stops other apps, not everyone. run-as works on any debuggable build, a rooted phone reads every sandbox, and the Capacitor 243,123 template's manifest sets android:allowBackup="true", so Android's Auto Backup copies shared_prefs/ to the user's cloud backup and device-to-device transfers. On iOS, Preferences is UserDefaults, a plist in the app container that ends up in unencrypted iTunes or Finder backups. The WebView's localStorage and IndexedDB are no better. Use Preferences for settings and the cart; put tokens, passwords and personal data in storage encrypted with a key the operating system guards.