Recently, there has been a notable shift in mobile application development practices. Rather than creating separate applications for each native platform, many developers are opting for hybrid mobile frameworks like React Native.
The rationale behind this shift is compelling, yet it introduces its own unique set of challenges, especially concerning security. In this article, we’ll explore some of the most prevalent security threats facing React Native mobile applications and discuss strategies for mitigating them.
Before we dive into the specific security issues, let’s first understand how React Native works under the hood. For a long time, at a high level, a React Native app consisted of two main parts: the JavaScript thread and the native bridge. The JavaScript thread ran the JS code, while the native bridge communicated with the native platform (iOS or Android) APIs.
The bridge was also responsible for transferring data, events, and commands between the two domains. The bridge enabled the use of native modules and components, which are platform-specific code or UI elements that can be accessed from JavaScript.
However, the bridge also brought along its share of challenges and constraints:
To tackle these issues, React Native recently introduced enhancements and an alternative architecture. This revamped approach includes:
Now that you know what goes on under the hood, let’s proceed.
React Native apps are not inherently more or less secure than native apps. React Native apps still share the same security risks and challenges as native apps, such as data leakage, unauthorized access, malicious attacks, and compliance violations. However, React Native apps also have some unique security considerations, due to the use of JavaScript.
As we mentioned earlier, React Native apps are built using JavaScript and React, which means that they may inherit some of the vulnerabilities and limitations of JavaScript, the underlying language of the framework.
JavaScript is a dynamic and interpreted language, which means that it is prone to errors, bugs, and code injection attacks. JavaScript also lacks some of the security features and mechanisms of other languages, such as type safety, memory management, access control, etc. One of the most common and dangerous JavaScript vulnerabilities is cross-site scripting (XSS).
Cross-site scripting (XSS) is a type of web-based attack that exploits the injection of malicious code into a web page or application. The attacker can use XSS to execute arbitrary code on the victim’s browser, steal cookies, session tokens, or other sensitive information, or redirect the user to a malicious website.
XSS is one of the most common and dangerous web vulnerabilities, and it affects both the server side and the client side of the web application. However, XSS is not a direct threat for React Native applications, as they do not run on a web browser, but on a JavaScript engine.
Therefore, the attacker cannot inject malicious code into the React Native application, as there is no HTML or DOM to manipulate. Moreover, React Native uses a virtual DOM to render the user interface, which prevents the execution of any inline or external scripts.
However, this does not mean that React Native applications are immune to XSS attacks, as they may still interact with web-based content or services.
For example, React Native applications may use WebViews, OAuth 2.0, or deep links.
WebViews are components that allow React Native apps to embed a web browser within the app, using the native web engines of the platforms, such as WebView on Android and WKWebView on iOS. When not correctly implemented, WebWiews can expose some of the app’s internal or sensitive information, such as cookies, headers, tokens, or URLs, to the web content or the web servers. This may result in data leakage, identity theft, or session hijacking.
To combat this issue, use the latest version of React Native WebView, which is the official and maintained module for WebViews in React Native.
Another example of web-based interaction is the use of the OAuth 2.0 protocol for authentication and authorization. OAuth 2.0 is a standard protocol for authorization, which allows users to grant limited access to their resources from one site to another site without exposing their credentials.
OAuth 2.0 is widely used by many web and mobile applications, such as Facebook, Google, Twitter, etc., to enable users to sign in or sign up with their existing accounts on other platforms.
OAuth 2.0 also introduces some security risks. Firstly, OAuth 2.0 relies on redirects, which are mechanisms that send users from one URL to another URL, to complete the authorization flow. Redirects may be vulnerable to phishing, spoofing, or hijacking attacks, which may trick users into visiting malicious or compromised websites, or intercept the authorization tokens or codes
Secondly, OAuth 2.0 requires the use of client IDs and client secrets, which are credentials that identify and authenticate the applications that use the protocol. Client IDs and client secrets may be exposed, leaked, or stolen, which may allow attackers to impersonate or abuse the applications, or access the users’ resources without their consent
Here is a way to prevent this:
Deep linking is a technique used to open specific screens or content within an app, using a URL or a link that is clicked by a user. It is useful for enhancing the user experience and the functionality of an app. Deep linking can also be used for authentication and authorization purposes, such as implementing OAuth 2.0 protocol and redirects, as discussed in the previous section.
Deep links are not secure and you should never send any sensitive information to them. Unprotected deep links may allow attackers to launch malicious or unwanted actions or requests within the app by crafting or manipulating the links or URLs that are clicked by the users.
To safeguard your deep links:
path
prop, which can match any link or URL to the screen or content of the appMan-in-the-middle (MITM) attacks are a type of attack that intercepts or alters the communication between two parties, such as the app and the server, or the app and the provider. MITM attacks can compromise the security and privacy of the communication, as they can access or manipulate the data, functionality, and session of the communication.
MITM attacks can also impersonate one or both parties and trick them into providing their credentials, personal information, or payment details. These attacks are common in network communication but they can also affect React Native apps.
React Native apps can use various methods and protocols to communicate with the server or the provider, such as HTTP, HTTPS, WebSocket, TCP, UDP, etc.:
These methods and protocols can be vulnerable to MITM attacks if they do not properly encrypt, authenticate, or verify the communication. So, React Native developers need to take the following preventive measures:
SSL pinning is a technique that allows mobile apps to verify the identity of the server they are communicating with by checking that the server’s certificate matches a known value. SSL pinning can also help prevent man-in-the-middle attacks.
There are two main ways of implementing SSL pinning: certificate pinning and public key pinning. Both have their pros and cons but certificate pinning is the preferred choice because of its ease of implementation. Also, it’s important to note that public key pinning is no longer supported by most browsers.
According to the React Native documentation:
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.
In React Native, you can achieve SSL pinning using react-native-ssl-pinning. This library implements React-Native SSL pinning and public key pinning using OkHttp 3 in Android, and AFNetworking on iOS.
Jailbreaking and rooting are processes that allow users to gain full access to the operating system and the file system of their devices, which can enable them to install unauthorized apps, modify system settings, and bypass security restrictions. It is also good to note that emulators are also rooted.
Note: There are a lot of users that jailbreak their devices so, not allowing your apps to run on a jailbroken device could have a significant impact on your user base.
A good tool to detect jailbroken and rooted devices is the freeRASP for React Native library by Talsec.
This is an important aspect of developing React Native apps, especially when dealing with sensitive information such as tokens, passwords, or payment details. You should avoid storing such data in plain text or using insecure storage solutions such as AsyncStorage
.
Instead, you should use a secure storage solution that encrypts the data at rest and protects it from unauthorized access.
Although iOS and Android provide tools to secure data stored on devices using Apple’s Keychain and Android’s Keystore respectively, these mechanisms make it more difficult to extract sensitive data from a device, but they should not be considered completely secure either as they can still be exploited (e.g., Apple Keychain exploit).
You can also make use of some RN wrappers around Keystone and Keystore such as:
AsyncStorage
, but with added security. Currently, it has a size limit of 2048 bytes per valueThere are a few reasons why you might want to include this security feature when building your apps:
The following are some ways you can implement it.
Android developers have more direct control over screenshot prevention by using flags and permissions.
Navigate to /android/app/src/main/java/com/{Project_Name}/MainActivity.java
and add the following lines of code:
import android.os.Bundle; import android.view.WindowManager; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE ); }
When it comes to iOS, things seem to become tricky as it goes against Apple’s design.
One way is to set an overlay screen in your AppDelegate.m
file. Consider this example:
- (void)applicationWillResignActive:(UIApplication *)application { // fill screen with our own colour UIView *colourView = [[UIView alloc]initWithFrame:self.window.frame]; colourView.backgroundColor = [UIColor whiteColor]; colourView.tag = 1234; colourView.alpha = 0; [self.window addSubview:colourView]; [self.window bringSubviewToFront:colourView]; // fade in the view [UIView animateWithDuration:0.5 animations:^{ colourView.alpha = 1; }]; } - (void)applicationDidBecomeActive:(UIApplication *)application { // grab a reference to our coloured view UIView *colourView = [self.window viewWithTag:1234]; // fade away colour view from main view [UIView animateWithDuration:0.5 animations:^{ colourView.alpha = 0; } completion:^(BOOL finished) { // remove when finished fading [colourView removeFromSuperview]; }]; }
Also, for devs building with Expo, the Expo ScreenCapture plugin is available. Not only does the library prevent screenshots, but it also notifies if a screenshot has been taken while in the foreground.
App tampering and repackaging are types of attacks that involve the modification or alteration of the application code or content, either by the user or by a third party. These attacks are possible in React Native apps, as they use JavaScript and React, which are dynamic and interpreted languages, which means that they are easy to read or modify.
App tampering and repackaging can be performed by using reverse engineering or tampering tools, such as Apktool, dex2jar, etc.
App tampering and repackaging can be done for various purposes, such as:
Here are two solutions that can be used to prevent app tampering:
With this API provided by Google, you can run checks to make sure that requests and actions performed by users are actually coming from an uncorrupted/unmodified binary of your Android application.
You can also use it to know if your app is running on a genuine device. To learn more about this API, head over to the documentation.
Source code obfuscation is a technique used to make easy-to-read code deliberately hard to understand and reverse engineer to prevent theft and modification.
Some options to consider are:
But you should note that obfuscation is not true security. At most, this approach will discourage some attackers but someone motivated enough can always reverse engineer the code — especially if an open source obfuscator is used, because it is known and de-obfuscators certainly already exist.
Another issue with this method is that obfuscation will make the code very difficult to interpret and optimize by the different JavaScript runtimes, resulting in a significant decline in the app’s performance.
When working in React Native apps, you will usually depend on already written third-party packages to perform various tasks. This can pose a threat if any of those packages have a vulnerability. Hence we need to employ ways to scan for these vulnerabilities and ensure that all third-party packages we use are safe.
Here are some tools you can use to achieve this:
Securing your React Native applications is not a one-time task but an iterative process that evolves with your application and the broader security landscape. Mobile apps written with React Native can be well-protected by employing the various strategies outlined in this article.
Happy hacking!
LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.
LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.
Start proactively monitoring your React Native apps — try LogRocket for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
One Reply to "Understanding security in React Native applications"
Hello Wisdom, this is a nice article on react native app security. I have one question for SSL pinning key security.
So, we do SSL pinning using public hash key. How to secure that hash key in iOS native and android native?