Open
Description
Currently, the react-native-firebase library stores Firebase Cloud Messaging (FCM) notification data in the SharedPreferences file named io.invertase.firebase.xml in plain text. This poses a security risk, as sensitive notification details and metadata can be easily accessed on a compromised device. There is no configuration option to disable this storage or to use encrypted storage (e.g., EncryptedSharedPreferences) by default.
Steps to reproduce:
- Trigger a background FCM notification.
- Observe that the notification data is stored in io.invertase.firebase.xml in plain text.
- Verify that no configuration option exists to encrypt or disable this storage.
We either need a configuration flag or secure defaults option that either:
- Prevents storing notification data in plain text altogether, or
- Uses EncryptedSharedPreferences to ensure that the data is encrypted at rest.
I was able to create a patch that converts encrypts the data saved on xml,
diff --git a/node_modules/@react-native-firebase/app/android/build.gradle b/node_modules/@react-native-firebase/app/android/build.gradle
index 98da166..0cf4288 100644
--- a/node_modules/@react-native-firebase/app/android/build.gradle
+++ b/node_modules/@react-native-firebase/app/android/build.gradle
@@ -103,6 +103,7 @@ dependencies {
implementation platform("com.google.firebase:firebase-bom:${ReactNative.ext.getVersion("firebase", "bom")}")
implementation "com.google.firebase:firebase-common"
implementation "com.google.android.gms:play-services-auth:${ReactNative.ext.getVersion("play", "play-services-auth")}"
+ implementation 'androidx.security:security-crypto:1.1.0-alpha03'
}
ReactNative.shared.applyPackageVersion()
diff --git a/node_modules/@react-native-firebase/app/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.java b/node_modules/@react-native-firebase/app/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.java
index 2c7d0ca..c9652f6 100644
--- a/node_modules/@react-native-firebase/app/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.java
+++ b/node_modules/@react-native-firebase/app/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.java
@@ -17,9 +17,14 @@ package io.invertase.firebase.common;
*
*/
-import android.content.Context;
import android.content.SharedPreferences;
+
import io.invertase.firebase.app.ReactNativeFirebaseApp;
+import androidx.security.crypto.EncryptedSharedPreferences;
+import androidx.security.crypto.MasterKey;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
public class UniversalFirebasePreferences {
private static final String PREFERENCES_FILE = "io.invertase.firebase";
@@ -79,11 +84,23 @@ public class UniversalFirebasePreferences {
}
private SharedPreferences getPreferences() {
- if (preferences == null) {
- preferences =
- ReactNativeFirebaseApp.getApplicationContext()
- .getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
+ try {
+ if (preferences == null) {
+
+ var context = ReactNativeFirebaseApp.getApplicationContext();
+ var masterKey = new MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build();
+
+ preferences = EncryptedSharedPreferences.create(
+ context,
+ PREFERENCES_FILE,
+ masterKey,
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
+ );
+ }
+ return preferences;
+ } catch (IOException | GeneralSecurityException e) {
+ throw new RuntimeException(e);
}
- return preferences;
}
}