How to store sensitive data in a React Native app

Sensitive data refers to confidential information that, if compromised or accessed by unauthorized individuals, can result in privacy breaches, identity theft, and other harmful consequences. Sensitive data includes user credentials, API keys, and access tokens. Storing sensitive data securely is crucial for several reasons:

  • It helps protect confidential information from unauthorized access.

  • It helps preserve customer trust. Customers trust organizations with their sensitive data, expecting it to be handled securely.

  • It ensures data integrity. Storing sensitive data securely enables organizations to verify the integrity of the data and ensure that it has not been tampered with.

We can store data in React Native using the AsyncStorage system, which is an unencrypted, asynchronous, persistent, key-value storage system that helps store data in the form of a string format. Read more about AsyncStorage here.

However, AsyncStorage is not specifically designed to store sensitive data securely. We can employ an additional security measure, such as strong encryption, to store sensitive data securely using AsyncStorage. We can encrypt our sensitive data before storing it, which will help protect it from being accessed by unauthorized individuals.

The react-native-crypto-js library

One way to encrypt data before storing it using AsyncStorage is to use the react-native-crypto-js library. This is a JavaScript library that provides various cryptographic algorithms, including AES encryption. Moreover, react-native-crypto-js is cross-platform compatible, i.e., it can be used to encrypt data on both Android and iOS operating systems.

To encrypt data using the react-native-crypto-js library, we have to import CryptoJS from react-native-crypto-js.

import CryptoJS from 'react-native-crypto-js';
Importing CryptoJS

Encryption key

In order to encrypt sensitive data, we first have to generate a strong encryption key. We can use the CryptoJS.lib.WordArray.random() method to do so. This method generates a random WordArrayA data structure used in CryptoJS to represent an array of 32-bit words. of a specified length in bytes. We can then convert it to a string using the toString() method.

The code snippet below shows how to generate a strong encryption key of 32 bytes (256 bits) using the CryptoJS.lib.WordArray.random method.

CryptoJS.lib.WordArray.random(256 / 8).toString();
Generating encryption key using the CryptoJS.lib.WordArray.random method

Encrypting data

Once the encryption key has been generated, we can use the CryptoJS.AES.encrypt() method to encrypt data. This method takes in the following two parameters:

  • value: This is the data that needs to be encrypted.

  • encryptionKey: This is the encryption key using which we encrypt the value.

Note: The CryptoJS.AES.encrypt method encrypts data in the form of bytes. We can convert it to a string using the toString() method.

The code snippet below shows how to encrypt data using the CryptoJS.AES.encrypt method.

CryptoJS.AES.encrypt(value, encryptionKey).toString();
Encrypting data using the CryptoJS.AES.encrypt method

Decrypting data

The CryptoJS.AES.decrypt() method can be used to decrypt the encrypted data. This method takes in the following two parameters:

  • encryptedValue: This is the encrypted data that needs to be decrypted.

  • encryptionKey: This is the encryption key using which we encrypted the data.

Note: The CryptoJS.AES.decrypt method decrypts data in the form of bytes. We can convert it to a string using the toString() method. Furthermore, we also have to convert the decrypted data to a readable encoding scheme using CryptoJS.enc.Utf8.

The code snippet below shows how to decrypt data using the CryptoJS.AES.decrypt method.

CryptoJS.AES.decrypt(encryptedValue, encryptionKey).toString(CryptoJS.enc.Utf8)
Decrypting data using the CryptoJS.AES.decrypt method

Code example

Let’s look at an example where we utilize AsyncStorage and the react-native-crypto-js library to store sensitive data in a React Native app.

/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * <p>This source code is licensed under the MIT license found in the LICENSE file in the root
 * directory of this source tree.
 */
package com.reactnativelearning;

import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.android.utils.FlipperUtils;
import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceEventListener;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;

/**
 * Class responsible of loading Flipper inside your React Native application. This is the debug
 * flavor of it. Here you can add your own plugins and customize the Flipper setup.
 */
public class ReactNativeFlipper {
  public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
    if (FlipperUtils.shouldEnableFlipper(context)) {
      final FlipperClient client = AndroidFlipperClient.getInstance(context);

      client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
      client.addPlugin(new DatabasesFlipperPlugin(context));
      client.addPlugin(new SharedPreferencesFlipperPlugin(context));
      client.addPlugin(CrashReporterPlugin.getInstance());

      NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
      NetworkingModule.setCustomClientBuilder(
          new NetworkingModule.CustomClientBuilder() {
            @Override
            public void apply(OkHttpClient.Builder builder) {
              builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
            }
          });
      client.addPlugin(networkFlipperPlugin);
      client.start();

      // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
      // Hence we run if after all native modules have been initialized
      ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
      if (reactContext == null) {
        reactInstanceManager.addReactInstanceEventListener(
            new ReactInstanceEventListener() {
              @Override
              public void onReactContextInitialized(ReactContext reactContext) {
                reactInstanceManager.removeReactInstanceEventListener(this);
                reactContext.runOnNativeModulesQueueThread(
                    new Runnable() {
                      @Override
                      public void run() {
                        client.addPlugin(new FrescoFlipperPlugin());
                      }
                    });
              }
            });
      } else {
        client.addPlugin(new FrescoFlipperPlugin());
      }
    }
  }
}
Storing sensitive data using AsyncStorage and the react-native-crypto-js library

Code explanation

  • Line 1: We import React and the useState Hook from the react library.

  • Lines 2–9: We import the required components from the react-native library.

  • Line 10: We import AsyncStorage from @react-native-async-storage/async-storage.

  • Line 11: We import CryptoJS from react-native-crypto-js.

  • Line 13: We use the CryptoJS.lib.WordArray.random method to generate a strong encryption key of 32 bytes (256 bits).

  • Line 17: We use the useState Hook to initialize the state variable key. We set the initial state as an empty string.

  • Line 18: We use the useState Hook to initialize the state variable value. We set the initial state as an empty string.

  • Line 20: We define the encrypt function. Inside the function, we use the CryptoJS.AES.encrypt method to encrypt value using the encryptionKey.

  • Line 22: We define the decrypt function. Inside the function, we use the CryptoJS.AES.decrypt method to decrypt encryptedValue using the encryptionKey.

  • Lines 24–40: We define the function storeData to store our sensitive data. If the values for key and value are empty, an error will be shown using the Alert box, and the function will return. Otherwise, we use the encrypt function to encrypt the data. Once encrypted, we use the setItem method of AsyncStorage to store data according to the values of key and value provided. Once successfully stored, a success message is shown using the Alert box. If there is any error while storing data, an error will be shown using the Alert box. Relevant error details are then logged using the console.log method.

  • Lines 42–60: We define the function fetchData to fetch our sensitive data. If the value for key is empty, an error will be shown using the Alert box, and the function will return. Otherwise, we use the getItem method of AsyncStorage to fetch data according to the value of the key provided. If the value returned by the getItem method is not null, we use the decrypt function to decrypt the fetched data. Once decrypted, we use the Alert box to display the decrypted value. If the value returned by the getItem method is null, we use the Alert box to show an error message. Furthermore, an error will be shown using the Alert box if there’s any error while fetching data. Relevant error details are then logged using the console.log method.

  • Lines 63–82: We write the logic for the user interface of the React Native application.

  • Lines 86–106: We define the styles for our user interface.

Note: We can download and execute the code example above on our local system or machine. Follow the official React Native documentation to set up the development environment. The code example above is for the Android operating system.

Free Resources

Copyright ©2024 Educative, Inc. All rights reserved