
Congratulations ๐, you have completed the development of your React Native app, you put in a very good amount of effort to make it responsive, fast and pixel perfect, but wait..! ๐ have you implemented security features on it or perform a security audit? If not then I'm sorry to say you havenโt completed your app ๐, your app is still in progress and has serious security vulnerabilities.
Securing your React Native app is a very important step you should take in order to make your application scalable.
Your app can be reverse engineered, your sensitive information can be revealed and your customer data can be breached. Even if you choose the best framework for developing a cross platform application, still your app is at risk.
Following are some ways to secure your React Native application.
Now we will explore all above security implementations with more details and also look at the code examples
Implementing SSL (Secure Sockets Layer) Certificate pinning means securing API calls. When a certificate is pinned, this will ensure that your application only communicates with the backend server that presents a trusted certificate. In easy words, with SSL pinning, the application verifies that the server's certificate matches a certificate that you pinned.
Below is the code example of how you can pin a certificate on an API call.
// This is a JavaScript code block
import RNSslPinning from 'react-native-ssl-pinning';
RNSslPinning.fetch('https://your-api.com', {
method: 'GET',
sslPinning: {
certs: ['your_server_cert']
}
})
.then(response => console.log(response))
.catch(error => console.log(error));
In above code snippet react-native-ssl-pinning library is used for pinning certificate and this api call will only communicate to backend server that match the pinned certificate.
Your APK or IPA file can be easily reverse engineered or decompiled, all the business logics and secrets like API Key can be visible. To prevent this we can obfuscate code. Code Obfuscation is the process of intentionally making your applications source code difficult to understand by humans. The goal is to protect your code from reverse engineering and tempering, making it harder for attackers to figure out how your app works.
Below code can be used for implementing code obfuscation. You need to add this code in metro.config.js file in the root folder, if the file is not present you can create one.
const obfuscatingTransformer = require('react-native-obfuscating-transformer');
module.exports = {
transformer: {
babelTransformerPath: obfuscatingTransformer.configure({
// Custom obfuscation settings
obfuscatorOptions: {
compact: true, // Removes unnecessary code to make it smaller
controlFlowFlattening: true, // Makes the control flow of the code harder to follow
deadCodeInjection: true, // Adds unused code to make it harder to understand
debugProtection: true, // Makes debugging harder
stringArray: true, // Converts strings to an array of encoded strings
stringArrayEncoding: ['base64'], // Encodes strings in base64
},
}),
},
resolver: {
sourceExts: ['js', 'json', 'ts', 'tsx'],
},
};
In above code you can see react-native-obfuscating-transformer library used for configuring options for code obfuscation. After these implementations if you build an app then code will be bundled with obfuscation enabled.
Normally we use react native async storage to store any data on local storage, but this stores data in plain form which means that if an app decompiles local storage data can be visible. To prevent storing data in plain form we can use alternative libraries that provide a secure way to store data on local storage. One library we have react-native-encrypted-storage for storing values. This library adds an additional security layer to data stored locally by encrypted plain data.
There is another library which is react-native-mmkv which also provides secure local storage and it is also faster than react native async storage.
Normally we store our API Key or Access Token in an ENV file or in javascript code in plain format. This is not a good practice; keys can be revealed even if you add keys on environment files.
There is good practice to store such type of sensitive information is to use the react-native-keychain library. This library provides access to the keychain (iOS) and KeyStore (Android) for securely storing credentials like passwords, tokens, or other sensitive information in React Native apps.
Secure session management in a React Native app means how you create, use, refresh and destroy a userโs login session safely. Most of the real security lives on the backend, but the mobile app must handle tokens correctly.
The following table shows where we can session data securely.
| Data | Where |
|---|---|
| Access Token | Secure Storage (Keychain / Keystore) |
| Refresh Token | Secure Storage (Keychain / Keystore) |
| User Profile | Async Storage (non-sensitive) |
| Session State | App State (Redux) |
Some mobile devices are rooted (Android) or JailBreak (iOS), these type of devices bypass security rules and sensitive information can be accessible, to prevent app running on jailbroken or rooted device we can use react-native-root-detecton this library detects if device is modified or not and return the boolean value for that.
Following code snippet shows that how you can detected modified devices.
import RootDetection from 'react-native-root-detection';
const checkRoot = async () => {
const isRooted = await RootDetection.isDeviceRooted();
if (isRooted) {
// Restrict features, log event, or warn user
}
}
We have explored the core ways of securing React Native applications, there are many other ways to secure apps. Always remember securing an app is not an adding extra feature on it is a necessary step, without security your app will always be at risk and you cannot scale it to a bigger level.