It’s impossible nowadays to build software that is 100% secure. The probability of being a victim of an attack or being exposed to a security vulnerability is proportional to the effort developers put into protecting the applications created i.e. in React Native against any attacks.
Building a mobile app is not only about creating screens, making fancy animations and processing data from the API. One of the biggest challenges in the development process is security. Also, it is one of the most often overlooked aspects when building software.
When you want to build a secure React Native app, you need to pay attention to many factors. It may require some understanding of iOS and Android native platforms and general security rules in terms of programming.
So, what can you do to make your React Native application more secure?
Environmental variables allow you to separate secrets from your source code. It is very useful when you have some API keys or other credentials that you may not want to share with other people. If you are building an open-source project, you can share source code while allowing other people to create their .env file.
It’s also very useful when you want to configure your app dynamically without changing the source code. For example, you can set different database URLs for each environment.
But it all makes sense only if you won’t forget to add the .env files to your .gitignore! Otherwise, you may end up with unauthorised access to many services your app is relying on. If you use Google a bit, you will find tons of stories of people losing money or data because of the secrets exposed on the repository.
How to correctly manage environmental variables in React Native? You can use one of the popular libraries out there, like react-native-dotenv. It’s really easy to integrate that library with your project and make your secrets safe!
Storing sensitive data
For persisted user data, you need to choose the right type of storage based on its sensitivity. You may need to use this data for offline support or save users’ access tokens, so they wouldn’t have to re-authenticate each time they use the app.
One of the most popular modules for storing data in React Native is Async Storage. The question is: is it secure enough, too?
Async Storage is an unencrypted and persistent key-value storage, which is available across the application. It is not shared between apps – each application has its sandbox environment and has no access to data from other apps.
More than that, it's a good idea for storing non-sensitive data across the application. It might be a Redux or GraphQL state or some global app-wide variables. On the other hand, you shouldn’t use that for storing tokens and secrets, as the storage is not encrypted in any way.
You probably started wondering about any more secure alternatives for Async Storage. The good news is that if you need some encryption, you can use Secure Store!
React Native itself does not come bundled with any kind of storage for sensitive data. However, there are some pre-existing solutions available. For iOS, there are Keychain Services, which allow storing small chunks of sensitive data securely. If you are wondering where to store tokens, passwords, and any other information – it’s the right place. Android has its equivalent called Shared Preferences.
In order to use Keychain Services or Shared Preferences, it is possible to use one of the available libraries:
They are very easy to implement in your project. If you are building your app with Expo, you should give expo-secure-store a try – if you have ever worked with Async Storage, the usage is pretty the same, except your data is safe!
What are some other pros of using Secure Store? Except for the obvious one (encryption), you may appreciate some other facts:
- The device must be unlocked to get access to the keychain.
- It is not possible to restore the keychain to a different device.
- In newer devices, encryption keys are stored on the hardware level rather than on the app level.
Even if you are using HTTPS endpoints, your data may be still vulnerable to interception. SSL pinning is a technique that can be used on the client-side to avoid man-in-the-middle attacks. It works by embedding a list of trusted certificates to the client during development. Only requests signed with one of the trusted certificates will be accepted, and any self-signed certificates won’t.
You should keep in mind that fetch and axios do not come with SSL pinning. To make it work in your app, you should consider using one of two libraries:
They are both easy to implement, using OkHttp3 on Android and AFNetworking on iOS to provide SSL pinning and cookie handling. The certificates need to be bundled inside the app.
When using SSL pinning, you should always remember about certificates expiration. They expire every 1-2 years, and you have to update them in the app and the server. Otherwise, if the certificate on the server has been updated, all the apps with the old certificates will stop working correctly.
Prevent access for rooted devices
There are many reasons people are rooting/jailbreaking their phones. If your app is working with some very sensitive data, you should consider protecting it with identification whether the device is rooted or not. These kinds of devices may gain unauthorised access to the data you are storing in your app.
To protect your app, you can use a library called jail-monkey. With just a few lines of code, you can:
- Identify if a phone has been jail-broken or rooted for iOS/Android,
- Detect if the device is faking its GPS location
- Detect if the application is running on external storage such as an SD card (Android only)
Banking apps make a good example – they are working with financial data, so most of them are not working on rooted devices.
It is challenging to create a bulletproof mobile app using React Native. However, if we put a little bit of effort and pay more attention to vulnerabilities, we can significantly reduce the possibility of a security breach. We should remember about security not only at the beginning of the project but at every stage, keeping in mind the bigger codebase, the bigger possibility of a security breach.