Security Aspects to consider for a React Native Application

Halak Vachhani
Simform Engineering
13 min readJun 3, 2021

Creating a mobile app is not only about navigation and animations (however important they are). One of the main concerns when developing a mobile app is data security. Especially when it comes to the data which are very sensitive and any security breach can lead to irreversible damage.

Almost all the app contains critical information of the users, so app security is the optimum element, especially while dealing with passcodes, touch ids, account names, credit card information and more. Many of the businesses opt to hire software development companies to make sure that they develop highly functional and feature rich apps. But they often overlook the fact that how secure their app is.

React native, like all JavaScript-based frameworks, is vulnerable to security threats. An analysis from a react native protection standpoint needs to take into consideration for the different parts of the framework as well as the connection between them.

Let me first list out the points which I’ll brief in detail one by one:

What are the common Security Issues while dealing with React Native applications?
- Storing Sensitive Information
- Selection of Local Storage for data persistence
- Deep Linking
- Android Specific Security Concerns
- IOS Specific Security Concerns
- Authentication Methods
- SSL Encryption & SSL Pinning

Why should we keep an eye on typical JavaScript vulnerabilities?

What do you need to build a secure React Native app?
- Having security expertise on every platform
- Minding the React Native specific vulnerabilities
- Managing the dependencies

Let’s take a deep dive to understand each of the above listed points in detail.

What are the common security issues while dealing with React Native applications?

Storing Sensitive Information

There are few cases where you need to define sensitive information in your code as mentioned below:

  • While building any application, it’s common that you have to make some server requests to get data and for that you will need to define endpoints of APIs in your application.
  • You often require some third party authentication integration, In that case you need to define an application secret which is created on a third party platform like Facebook, google in your application to access their services.
  • Often you require to use some services of open platforms like firebase, google to implement features like notification, messaging, analytics in your application. Here also you need to define the secret of an application created on open platform in your application to access their services.

Let’s look into security issues while dealing with sensitive information.
Never store sensitive keys or secrets in your app code. Anything included in your code could be accessed in plain text by anyone inspecting the app bundle.

If someone succeeds in messing with your API endpoints, it will bring everything to a screeching halt. Security breaches can also take a lot of time to recover from, which translates to suicide in business terms. While it’s true that most businesses probably won’t be affected by an hour or two of downtime, for some, it’s not permissible.

Security solutions to overcome sensitive information security issues.

  • To secure API endpoints we need to use tools like react-native-dotenv and react-native-config
  • To define secrets of open platform services like Facebook, google, firebase will use the same solution which we will discuss as an alternative to Async Storage. Because here also will store these secrets in local device storage.

Note:
react-native-config module doesn’t encrypt secrets for packaging, so do not store sensitive keys in .env file.

Selection of Local Storage for data persistence

You’ll often find the need to save data on the device, whether to support your app being used offline, cut down on network requests or save your user’s access token between sessions so they wouldn’t have to re-authenticate each time they use the app. In React Native we use Async Storage to save these types of data in the device.

Let’s look into security issues while dealing with local storage
Async Storage is an asynchronous unencrypted key-value store. Async Storage uses an unencrypted approach, it can make your data more vulnerable to being accessed by attackers. We can save information like non-sensitive data, redux state, Graph-QL state or global application level variables in Async Storage. But for sensitive information like tokens and secrets we need to choose another way.

Security solutions to overcome storage security issues
React Native does not provide any solution for sensitive data storage. There are pre-existing solutions in Android and iOS platforms which we can use with React Native by setup it in native side.

iOS Keychain
In iOS we use Keychain Services which allows you to securely store sensitive information like certificates, tokens, passwords, and any other sensitive information like secrets of open platform services which we use in our application.

Android Keystore
The Android Keystore system lets you store cryptographic keys in a container to make it more difficult to extract from the device. Once keys are in the keystore, they can be used for cryptographic operations with the key material remaining non-exportable.

In order to use iOS Keychain services or Android Secure Shared Preferences, you can either write a bridge yourself or use a library which wraps them for you and provides a unified API.

Here are some libraries to consider:
- react-native-encrypted-storage
- react-native-keychain
- redux-persist-sensitive-storage

Note:
redux-persist-sensitive-storage uses react-native-sensitive-info with redux-persist. react-native-sensitive-info manages all data stored in Android Shared Preferences and iOS Keychain. Android Shared Preferences are not secure, but there is a branch of react-native-sensitive-info that uses the Android key-store instead of shared preferences. You can use that branch with redux-persist-sensitive-storage if you prefer.

Deep Linking

Deep linking is a way of sending data directly to a native application from an outside source. A deep link looks like app:// where app is your app scheme and anything following the // could be used internally to handle the request.

For example, if you were building an E-commerce app, you could use app://products/1 to deep link to your app and open the product detail page for a product with id 1.

Deep links are not secure and you should never send any sensitive information in them.

Let’s look into security issues while dealing with deep linking
The reason deep links are not secure is because there is no centralised method of registering URL schemes. As an application developer, you can use any URL scheme you choose by configuring it in Xcode for iOS or adding an intent on Android.

Malicious app can hijack your data by also using the same scheme and then obtaining access to the data your link contains. Sending something like app://products/1 is not harmful, but sending tokens is a security concern.

iOS allows one single URL Scheme to be claimed by multiple apps. For instance, Sample:// can be used by two completely separate apps in their implementation of URL Schemes. This is how some malicious apps can take advantage of the URL Scheme and compromise users.

Security solutions to overcome deep linking security issues
Apple introduced Universal Links in iOS 9 as a solution to the lack of graceful fallback functionality in custom URI scheme deep links. Universal Links are standard web links that point to both a web page and a piece of content inside an app. When a Universal Link is opened, iOS checks to see if any installed app is registered for that domain. If so, the app is launched immediately without ever loading the web page. If not, the web URL (which can be a simple redirect to the App Store) is loaded in Safari.

Setting up a universal link (HTTP or HTTPS) login interface, and musing a random identifier to authenticate the received login token locally, prevents hijacking and malicious login token replaying.

Android Specific Security Concerns

Here we will discuss about how to protect our APK or app bundle placed over play store from reverse engineering attacks.

Hackers can easily access our Codebase by doing reverse engineering with APK or app bundle file. To avoid it we can add Pro Guard rules. Pro Guard rules are like a security shield for any android application. Basically, it obfuscates your code. So if someone reverse engineer it, it’s not readable and saves you from engineering attacks. There is also another benefit of using Pro Guard is that it reduces APK size by removing unused code and resources. If your project contain any third party library then you can add the Pro Guard rules of that library in your rules file.

To enable Pro Guard rule we have to enable the minifyEnabled property in app/build.gradle file.

iOS Specific Security Concerns

Here we will discuss about how we can restrict the insecure domains usage from iOS side. It will save us from transport layer attacks. You can restrict insecure domains by configuring some policies within your Info.plist file.

Now, let’s discuss what you should add in your Info.plist file for that.

From iOS 9.0 Apple has introduced one dictionary called the NSAppTransportSecurity which you can find inside info.plist file. Inside NSAppTransportSecurity there is one key NSAllowArbitraryLoads which is set to NO as default that means you have agreed with security benefits. In some scenarios when you are working with localhost or with HTTP domain if required then you have to make it YES otherwise you can’t make network request with those insecure domains.

There are chances that your app might get rejected while uploading it to apple store because you set NSAllowArbitraryLoads value as YES. To overcome that you can use NSExceptionDomains by providing a list of domains inside that. Application will consider like you have agreed to all over security benefits excepts the domain those domains which you have mentioned in NSExceptionDomains (although you have set NSAllowArbitraryLoads value as YES).

Authentication Methods

The OAuth-2 authentication protocol is incredibly popular nowadays, prided as the most complete and secure protocol around. The OpenID Connect protocol is also based on this. In OAuth-2, the user is asked to authenticate via a third party. On successful completion, this third party redirects back to the requesting application with a verification code which can be exchanged for a JWT — a JSON Web Token.

Let’s look into security issue while dealing with authentication method
On the web, this redirect step is secure, because URLs on the web are guaranteed to be unique. This is not true for apps because, as mentioned earlier, there is no centralised method of registering URL schemes! In order to address this security concern, an additional check must be added.

Security solutions to overcome authentication security issues
To be more secure with OAuth-2 approach as an extension we can use SHA 256 cryptographic algorithm which ensures that the authentication and token exchange requests come from the same client. SHA 256 creates a unique “signature” for a text or file. The signature is always the same length and for same inputs signature will always be same. This whole process is known as Proof of Key Code Exchange(PKCE).

Now, let’s understand how it works.
For that first here we have to discuss two terminology:

  • code verifier: A large random string generated by the client
  • code challenge: The SHA 256 of the code verifier

Steps to handle OAuth-2 with Proof of Key Code Exchange(PKCE).
1. Create Code Verifier: Code Verifier is a random key which we have to send with OAuth-2 request to get request tokens.

2. Create Code Challenge: Code Challenge is generated from the code verifier as shown below which will be used to request Authorisation Code.

3. Authorise User: To authorise the user, your app must send the user to the authorisation URL, including the Code Challenge you generated in the previous step and the method you used to generate the Code Challenge.

Response: You will get HTP 302 as response with authorisation code included in the URL

4. Request tokens: Here we have authorisation code which we will use to exchange tokens

Response: You will get response as shown below. ID Token contains the user information.Access Tokens are used to call the another endpoints.Refresh Token used to get the new access token if previous one is expired.

5. Call API: Now you can call another end points by using that access token.

A library to consider for OAuth is react-native-app-auth. React-native-app-auth is an SDK for communicating with OAuth-2 providers. It wraps the native AppAuth-iOS and AppAuth-Android libraries.

Note:
React-native-app-auth can support Proof of Key Code Exchange(PKCE) only if your Identity Provider supports it.

SSL Encryption & SSL Pinning

SSL encryption protects against the requested data being read in plain text between when it leaves the server and before it reaches the client. Using https endpoints could still leave your data vulnerable to interception.

Let’s look into security issues while dealing with network activity like SSL Encryption and Pinning
We use SSL Encryption by keeping in mind Network Security, With https, the client will only trust the server if it can provide a valid certificate that is signed by a trusted Certificate Authority that is pre-installed on the client. An attacker could take advantage of this by installing a malicious root CA certificate to the user’s device, so the client would trust all certificates that are signed by the attacker. Thus, relying on certificates alone could still leave you vulnerable.

Security solution for SSL encryption and SSL Pinning issues
SSL pinning is a technique that can be used on the client side to avoid this attack. It works by embedding (or pinning) a list of trusted certificates to the client during development, so that only the requests signed with one of the trusted certificates will be accepted, and any self-signed certificates will not be accepted.

Note:
When using SSL pinning, you should be mindful of certificate expiry. Certificates expire every 1–2 years and when one does, it’ll need to be updated in the app as well as on the server. As soon as the certificate on the server has been updated, any apps with the old certificate embedded in them will cease to work.

To implement SSL Pinning in React Native you can refer to react-native-ssl-pinning.

Why should we keep an eye on typical JavaScript vulnerabilities?

As React Native apps are using JavaScript, the security analysis of RN apps includes a search for typical JS-related vulnerabilities, like XSS attacks.

Generally, the attack surface is pretty wide for pure JavaScript applications. It narrows down for ReactJS, and it narrows down even more for React Native.

For example, React Native source code doesn’t use HTML elements ,Typical browser-based XSS vectors (ex. based on href attribute) which are relevant to ReactJS. It makes sense because React Native apps are not browser-based, they only run JavaScript code.

Even though React Native apps are associated with an adequate level of protection against XSS(Cross-site scripting) attacks, developers can use potentially dangerous API in JavaScript code, like the eval() function, which can steal all the data from local storage (Async Storage) by exploiting eval-based injection.

Linters and static code analysers can detect usage of dangerous functions like eval(), and notify developers.

What do you need to build a secure React Native app?

Having security expertise on every platform

Implementing security controls requires profound knowledge about every platform: iOS, Android, React Native. Development team should contain highly experienced React Native engineers with expertise in native security controls.

Minding the React Native specific vulnerabilities

Investing time into a proper risk assessment exercise, having a Risks and Threats Model (based on things you’ve learnt here) will help your team to visualise the security landscape and simplify security decision making.

Managing the dependencies

In React Native we generally use many dependencies, regularly updating it will save us from vulnerable attacks because it might be possible that many dependencies identified some vulnerable and solved it after you have added them in your project.

For Example, react-native-webview — is a React Native WebView component for iOS, Android, macOS, and Windows. It internally uses Android WebView system component which allows cross-origin iframes to execute arbitrary JavaScript in the top-level document due to this it is vulnerable to Cross-site Scripting (XSS).

This vulnerability affects React Native apps which use a
react-native-webview(< 11.0.0 version) that allows navigation to arbitrary URLs, and when that app runs on systems with an Android WebView version prior to 83.0.4103.106. To avoid that you have to upgrade it to 11.0.0

Establish a process for selecting, researching, updating, and replacing the dependencies. Use automated dependency analysers and code scanning tools to detect known vulnerabilities and patch dependencies.

Conclusion

Mobile apps written with React Native can be well-protected. It has its own cost and additional risks. To make your app secure, you need to follow the best practices of secure software development lifecycle, define and address possible risks, plan your security controls, and prepare a remediation strategy. Invest your time in building a threat model for your application as it helps to bring together security and usability of the app.

If you find this blog useful and worth reading then don’t forget to clap and share. If you have other security aspect in your mind that we should consider while building React Native app then please comment it.

--

--