Android – An Overview Of Application Security Best Practices

Hello Readers, CoolMonkTechie heartily welcomes you in this article (An Overview Of Application Security Best Practices).

In this article, we will learn about the best practices of Application Security in Android. By making our application more secure in android, we help preserve user trust and device integrity. This article explains about few best practices that have a significant, positive impact on our application’s security.

To understand the Application Security Best Practices, we cover the below topics as below :

  • Enforce secure communication with other applications
  • Provide the right permissions
  • Store data safely
  • Keep the services and related dependencies up-to-date

A famous quote about learning is :

“Being a student is easy. Learning requires actual work.”

So Let’s begin.

1. Enforce secure communication with other applications

When we safeguard the data that we want to exchange between our application and other applications, or between our application and a website, we improve our application’s stability and protect the data that we want to send and receive.

1.1 Use implicit intents and non-exported content providers

1.1.1 Show an application chooser

Use implicit intents to show application chooser that provides option to user to launch at least two possible applications on the device for the requested action. This allows users to transfer sensitive information to the application that they trust.

val intent = Intent(Intent.ACTION_SEND)
val possibleActivitiesList: List<ResolveInfo> =
        packageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL)

// Verify that an activity in at least two applications on the user's device
// can handle the intent. Otherwise, start the intent only if an application
// on the user's device can handle the intent.
if (possibleActivitiesList.size > 1) {

    // Create intent to show chooser.
    // Title is something similar to "Share this photo with".

    val chooser = resources.getString(R.string.chooser_title).let { title ->
        Intent.createChooser(intent, title)
    }
    startActivity(chooser)
} else if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent)
}

1.1.2 Apply signature-based permissions

Apply signature-based permissions while sharing data between two applications that is controlled by us. These permissions do not need user confirmation, but instead it checks that the applications accessing the data are signed using the same signing key. Hence offer more streamlined and secure user experience.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
    <permission android:name="my_custom_permission_name"
                android:protectionLevel="signature" />

1.1.3 Disallow access to our application’s content providers

Unless we intend to send data from our application to a different application that we don’t own, we should explicitly disallow other developers’ apps from accessing the ContentProvider objects that our application contains. This setting is particularly important if our application can be installed on devices running Android 4.1.1 (API level 16) or lower, as the android:exported attribute of the <provider> element is true by default on those versions of Android.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
    <application ... >
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.myapp.fileprovider"
            ...
            android:exported="false">
            <!-- Place child elements of <provider> here. -->
        </provider>
        ...
    </application>
</manifest>

1.2 Ask for credentials before showing sensitive information

When we are requesting the credentials so that we can access sensitive information or premium content in our application, ask for either a PIN/password/pattern or a biometric credential, such as using face recognition or fingerprint recognition.

1.3 Apply network security measures

Ensure network security with Security with HTTPS and SSL — For any kind of network communication we must use HTTPS (instead of plain http) with proper certificate implementation. This section describes how we can improve our application’s network security.

1.3.1 Use SSL traffic

If our application communicates with a web server that has a certificate issued by a well-known, trusted CA, the HTTPS request is very simple:

val url = URL("https://www.google.com")
val urlConnection = url.openConnection() as HttpsURLConnection
urlConnection.connect()
urlConnection.inputStream.use {
    ...
}

1.3.2 Add a network security configuration

If our application uses new or custom CAs, we can declare our network’s security settings in a configuration file. This process allows us to create the configuration without modifying any application code.

To add a network security configuration file to our application, we can follow these steps:

  1. Declare the configuration in our application’s manifest:
<manifest ... >
    <application
        android:networkSecurityConfig="@xml/network_security_config"
        ... >
        <!-- Place child elements of <application> element here. -->
    </application>
</manifest>

2. We add an XML resource file, located at res/xml/network_security_config.xml.

Specify that all traffic to particular domains should use HTTPS by disabling clear-text:

<network-security-config>
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">secure.example.com</domain>
        ...
    </domain-config>
</network-security-config>

During the development process, we can use the <debug-overrides> element to explicitly allow user-installed certificates. This element overrides our application’s security-critical options during debugging and testing without affecting the application’s release configuration.

The following snippet shows how to define this element in our application’s network security configuration XML file:

<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="user" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>

1.3.3 Create own trust manager

Our SSL checker shouldn’t accept every certificate. We may need to set up a trust manager and handle all SSL warnings that occur if one of the following conditions applies to our use case:

  • We’re communicating with a web server that has a certificate signed by a new or custom CA.
  • That CA isn’t trusted by the device we’re using.
  • You cannot use a network security configuration.

1.4 Use WebView objects carefully

Whenever possible, we load only allowlisted content in WebView objects. In other words, the WebView objects in our application shouldn’t allow users to navigate to sites that are outside of our control. In addition, we should never enable JavaScript interface support unless we completely control and trust the content in our application’s WebView objects.

1.4.1 Use HTML message channels

If our application must use JavaScript interface support on devices running Android 6.0 (API level 23) and higher, use HTML message channels instead of communicating between a website and your app, as shown in the following code snippet:

val myWebView: WebView = findViewById(R.id.webview)

// channel[0] and channel[1] represent the two ports.
// They are already entangled with each other and have been started.
val channel: Array<out WebMessagePort> = myWebView.createWebMessageChannel()

// Create handler for channel[0] to receive messages.
channel[0].setWebMessageCallback(object : WebMessagePort.WebMessageCallback() {

    override fun onMessage(port: WebMessagePort, message: WebMessage) {
        Log.d(TAG, "On port $port, received this message: $message")
    }
})

// Send a message from channel[1] to channel[0].
channel[1].postMessage(WebMessage("My secure message"))

2. Provide the right permissions

Application should request only the minimum number of permissions necessary to function properly.

2.1 Use intents to defer permissions

It should not add a permission to complete an action that could be completed in another application. Instead, we use an intent to defer the request to a different application that already has the necessary permission.

For example, If an application requires to create a contact to a contact application, it delegates the responsibility of creating the contact to a contacts application, which has already been granted the appropriate WRITE_CONTACTS permission.

// Delegates the responsibility of creating the contact to a contacts application,
// which has already been granted the appropriate WRITE_CONTACTS permission.
Intent(Intent.ACTION_INSERT).apply {
    type = ContactsContract.Contacts.CONTENT_TYPE
}.also { intent ->
    // Make sure that the user has a contacts application installed on their device.
    intent.resolveActivity(packageManager)?.run {
        startActivity(intent)
    }
}

In addition, if our application needs to perform file-based I/O – such as accessing storage or choosing a file – it doesn’t need special permissions because the system can complete the operations on our application’s behalf. Better still, after a user selects content at a particular URI, the calling application gets granted permission to the selected resource.

2.2 Share data securely across applications

We can follow these best practices in order to share our application’s content with other applications in a more secure manner:

  • Enforce read-only or write-only permissions as needed.
  • Provide clients one-time access to data by using the FLAG_GRANT_READ_URI_PERMISSION and FLAG_GRANT_WRITE_URI_PERMISSION flags.
  • When sharing data, we use “content://” URIs, not “file://” URIs. Instances of FileProvider do this for us.

The following code snippet shows how to use URI permission grant flags and content provider permissions to display an application’s PDF file in a separate PDF Viewer application:

// Create an Intent to launch a PDF viewer for a file owned by this application.
Intent(Intent.ACTION_VIEW).apply {
    data = Uri.parse("content://com.example/personal-info.pdf")

    // This flag gives the started application read access to the file.
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}.also { intent ->
    // Make sure that the user has a PDF viewer application installed on their device.
    intent.resolveActivity(packageManager)?.run {
        startActivity(intent)
    }
}

3. Store data safely

Although our application might require access to sensitive user information, our users will grant our application access to their data only if they trust that we’ll safeguard it properly.

3.1 Store private data within internal storage

We need to store all private user data within the device’s internal storage, which is sandboxed per application. Our application doesn’t need to request permission to view these files, and other applications cannot access the files. As an added security measure, when the user uninstalls an app, the device deletes all files that the app saved within internal storage.

We consider working with EncryptedFile objects if storing data is particularly sensitive or private. These objects are available from Security library instead of File objects.

For Example, one way to write data to storage demonstrates in the below code snippet:

// Although you can define your own key generation parameter specification, it's
// recommended that you use the value specified here.
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

// Create a file with this name, or replace an entire existing file
// that has the same name. Note that you cannot append to an existing file,
// and the file name cannot contain path separators.
val fileToWrite = "my_sensitive_data.txt"
val encryptedFile = EncryptedFile.Builder(
    File(DIRECTORY, fileToWrite),
    applicationContext,
    mainKeyAlias,
    EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()

val fileContent = "MY SUPER-SECRET INFORMATION"
        .toByteArray(StandardCharsets.UTF_8)
encryptedFile.openFileOutput().apply {
    write(fileContent)
    flush()
    close()
}

Another example shows the inverse operation, reading data from storage:

// Although you can define your own key generation parameter specification, it's
// recommended that you use the value specified here.
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

val fileToRead = "my_sensitive_data.txt"
val encryptedFile = EncryptedFile.Builder(
    File(DIRECTORY, fileToRead),
    applicationContext,
    mainKeyAlias,
    EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()

val inputStream = encryptedFile.openFileInput()
val byteArrayOutputStream = ByteArrayOutputStream()
var nextByte: Int = inputStream.read()
while (nextByte != -1) {
    byteArrayOutputStream.write(nextByte)
    nextByte = inputStream.read()
}

val plaintext: ByteArray = byteArrayOutputStream.toByteArray()

3.2 Store data in external storage based on use case

We consider external storage for large, non-sensitive files that are specific to our application, as well as files that our application shares with other applications. The specific APIs that we use depend on whether our application is designed to access app-specific files or access shared files.

3.2.1 Check availability of storage volume

When user interacts with a removable external storage device from the application, then he might remove the storage device while our app is trying to access it. We need to include logic to verify that the storage device is available.

3.2.2 Access application-specific files

If a file doesn’t contain private or sensitive information but provides value to the user only in our application, we store the file in an application-specific directory on external storage.

3.2.3 Access shared files

If our application needs to access or store a file that provides value to other applications, we can use one of the following APIs depending on our use case:

  • Media files: To store and access images, audio files, and videos that are shared between apps, use the Media Store API.
  • Other files: To store and access other types of shared files, including downloaded files, use the Storage Access Framework.

3.2.4 Check validity of data

If our application uses data from external storage, make sure that the contents of the data haven’t been corrupted or modified. Our application should also include logic to handle files that are no longer in a stable format.

We take an example of hash verifier in below code snippet:

val hash = calculateHash(stream)
// Store "expectedHash" in a secure location.
if (hash == expectedHash) {
    // Work with the content.
}

// Calculating the hash code can take quite a bit of time, so it shouldn't
// be done on the main thread.
suspend fun calculateHash(stream: InputStream): String {
    return withContext(Dispatchers.IO) {
        val digest = MessageDigest.getInstance("SHA-512")
        val digestStream = DigestInputStream(stream, digest)
        while (digestStream.read() != -1) {
            // The DigestInputStream does the work; nothing for us to do.
        }
        digest.digest().joinToString(":") { "%02x".format(it) }
    }
}

3.3 Store only non-sensitive data in cache files

To provide quicker access to non-sensitive application data, we store it in the device’s cache. For caches larger than 1 MB in size, we use getExternalCacheDir() otherwise, use getCacheDir(). Each method provides the File object that contains our application’s cached data.

Let’s take one example code snippet that shows how to cache a file that application recently downloaded:

val cacheFile = File(myDownloadedFileUri).let { fileToCache ->
    File(cacheDir.path, fileToCache.name)
}

If we use use getExternalCacheDir() to place our application’s cache within shared storage, the user might eject the media containing this storage while our application run. We should include logic to gracefully handle the cache miss that this user behavior causes.

3.4 Use SharedPreferences in private mode

When we are using getSharedPreferences() to create or access our application’s SharedPreferences objects, use MODE_PRIVATE. That way, only our application can access the information within the shared preferences file.

Moreover, EncryptedSharedPreferences should be used for more security which wraps the SharedPreferences class and automatically encrypts keys and values.

4. Keep services and dependencies up-to-date

Most applications use external libraries and device system information to complete specialized tasks. By keeping our app’s dependencies up to date, we make these points of communication more secure.

4.1 Check the Google Play services security provider

If our application uses Google Play services, make sure that it’s updated on the device where our application is installed. This check should be done asynchronously, off of the UI thread. If the device isn’t up-to-date, our application should trigger an authorization error.

4.2 Update all application dependencies

Before deploying our application, make sure that all libraries, SDKs, and other dependencies are up to date:

  • For first-party dependencies, such as the Android SDK, we use the updating tools found in Android Studio, such as the SDK Manager.
  • For third-party dependencies, we check the websites of the libraries that our app uses, and install any available updates and security patches.

That’s all about in this article.

Related Other Articles / Posts

Conclusion

In this article, we understood about the best practices of Application Security in Android. This article explained about few best practices that every mobile app developer must follow to secure the application from vulnerability. This helps us to develop the highly secure applications required to prevent valuable user information of our application and maintain the trust of our client.

Thanks for reading! I hope you enjoyed and learned about the best practices of Application Security concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find other articles of CoolMonkTechie as below link :

You can also follow official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Technology Tips And Tricks – Best Security Checklist for Mobile Development Here​​

Hello Readers, CoolMonkTechie heartily welcomes you in Technology Best Tips and Tricks Series.

In this series article, we will discuss about Security checklist of mobile application which helps every developers to build secure mobile application. Almost every business has a mobile app on which it gives various features and facilities to its customers. The app stores display and transmit sensitive data. A business app that does not use proper security protections can put corporate resources and personal information at risk, which can lead to fines.

A Famous quote about Science is :

“Prevention is better than cure.”


So Let’s begin.

Overview

Security is always a concern when creating an application, but it’s often overlooked when developing the application. And what’s overlooked in the beginning becomes a dormant vulnerability later on that may threaten your business, but you might not be able to catch it then before something happens.

With ever-advancing mobile technology, mobile application security has become a vital topic that every major enterprise must consider and understand.

Sensitive information stored on a device could be lost or stolen, which can lead to a data breach, compliance violations, and expensive and/or embarrassing public disclosures.

Large organizations acknowledge mobile device threats and vulnerabilities and perceive that they have correct security protection.

Corporations nowadays leverage mobile applications to distribute relevant, critical data to their workforce, partners, or customers.The productivity regarding mobile devices comes at a price — security risk increases.

Mobile applications create yet another path into enterprise networks, allowing criminals, fraudsters, and hackers to propagate malicious code.

For that reason, it’s often best to account for security from the very start and it’s definitely not a time waste.


Mobile App Security Standards/Checklist:

There are a few practices that you could follow when creating an application that will help you create more secure applications on the go. Here’s the list that you could follow:


1. Securing the source code

Creating an impactful app and following every guideline to make it secure while leaving the source code open to anyone can result in security risks.

Most of the source code is often on the client side, such as UI and business logic. If this sensitive information gets into the wrong hands like hackers, then it could damage your business.

There is a process known as obfuscation, where the source code is changed in such a way so that it confuses the person who tries to read it and do some changes in it.

It does alteration in classes, attribute names to meaningless characters or names. The whole aim of this is to make the code too confusing so that nobody can understand it.


2. Securing the files and the database

It’s not enough to secure the code base, you also need to secure the data. You need to store data on the device for all sort of reasons, this data can include critical information such as user credentials or payment info, for that reason you should always make sure that the data you’re storing on the user’s end is encrypted to prevent its leakage.


3. Securing Communications

Network security in mobile development is not as trivial as it is for web development, and many companies and developers do not opt network security in their development process. It’s not enough to secure the data on the generation and storage points only.

Your application’s data should also be secured in transit, that means that sending and receiving data inside your application should be via secure mediums, with a VPN tunnel, SSL, TLS or HTTPS communication. This way, if anyone managed to eavesdrop on your network requests, they wouldn’t be able to decipher the data out and security will be ensured, otherwise, attacks such as packet-sniffing and man-in-the-middle would be a serious threat to your application. 


4. Consider Data Portability

Data portability is the practice of using user data across different platforms and services. Like using your Facebook account to sign in other platforms like StackOverflow or GitHub. This allows you to leverage the security of the bigger companies and use it on your side, inside of implementing all the user’s authentication and private data all from scratch, it also makes it easier for the user as more people find it plausible to use their old accounts than create new ones.

A popular protocol for that is OAUTH.The simple flow of OAuth allows you to access the protected resources a.k.a user data on the other end by just storing the access token, which saves you the hassle of collecting and protecting that data.


5. Brace for Reverse Engineering

This might be more specific to Android applications since Android is an open source platform, which means anyone can look up the source code, make modifications on the OS any way they want. For this reason, you’ll need an understanding of the Java-based Android environment as well as of the Linux os kernel to understand the process and understand how you can protect your application against reverse engineering.


6. Perform Input Validation

Input validation is one of the most important practices of taking a user’s input, yet it’s often disregarded in the development process for the sake of “speed”. Input validation allows you to check the data supplied by the user to prevent malformed data. Input validation is very common in most frameworks, both on the web and mobile development and you should make use of it.


7. Use Cryptography wisely

Encrypting your data, or hashing the passwords doesn’t necessarily dictate that your application is secure. In fact, broken cryptography is the most common threat to mobile applications. You should avoid weak or broken algorithms and make sure that your program doesn’t use them. These algorithms include MD5, MD4, SHA1, BLOWFISH, RC2, and RC4. Cryptography is a strong element of security in a mobile application, and hence, if used correctly it can protect your application and data.


8. Implement strong authentication and authorization systems

One of the most important steps towards application security is to use strong authentication and authorization systems that consider salient features like privacy, session management, identity management, and device security.


9. Understand the platform and frameworks

Most of the mobile apps developed nowadays run either on Google Android or Apple iOS. Fewer run on Windows devices and Blackberry devices. Organizations try to create apps that run on multiple operating systems. These apps are known as hybrid apps. There are various companies that provide you hybrid app development services.

Mobile developers need to understand how security works on each targeted OS and the various risks that can come in these apps. Preparation against security threats can reduce them to a large extent.

Security has always been an issue in the IT industry. And at present, hackers are using newer methods to hack various applications. Hence, it is must to stay updated with all the possible security checklists.


10. Perform Penetration Testing

Penetration testing is one of the most important stages of securing an application as it can scan a wide range of vulnerabilities. It simulates what an attacker can do in various environments and modes of operation. A lot of people confusing regular software testing with penetration testing, but they are really different and serve different purposes, but you need to do both.

That’s all about in this article.


Conclusion

In this article, We understood about security checklist for mobile app development.

Security is everyone’s concern, it’s true that most users wouldn’t regard the permissions given by the application they’re using, and they can’t possibly tell if an application is secure or not. But should a leakage happen, it is going to be your responsibility as a developer.

There are various ways that you can consider in order to make your mobile app secure:

  • Make your source code secure using obfuscation. It is a method in which the source code is changed in a form which is quite confusing. Hence, in case, your code is in wrong hands, he would not be able to misuse it.
  • Use cryptography in a smart way. Don’t use weak algorithms while using this technique. Cryptography when used in a proper way, can provide high-level security to mobile apps.
  • Protect app data on the device. Do not store sensitive data on your mobile app. If necessary, encrypt it with the latest encryption technologies.
  • Penetration testing. It is different from normal testing and is quite effective in making a mobile app secure.
  • Utilizing the data portability. Using Data portability, you can use the security offered by big companies like Facebook and Google.
  • Making communication secure. Use VPN, SSL, and HTTPS in order to make the transmission of data through a secure medium.
  • Understand the platforms and frameworks. If you create hybrid apps, you need to understand how security works on every focused operating system.

Thanks for reading ! I hope you enjoyed and learned about Mobile Application Security Checklist. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

React Native – How To Secure Mobile Application In React Native ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, we will learn how to secure Mobile application in React Native. We will discuss Security related areas and techniques to secure mobile application. React Native is a popular cross-platform JavaScript framework. Components of React Native apps are rendered in Native UI. In this article, we will focus on the security side of the framework. In this article, we will focus on the security side of the framework.

A famous quote about learning is :

” Develop a passion for learning. If you do, you will never cease to grow.”

So Let’s begin.


Analyzing React Native

React Native has an alternative approach for cross-platform development. Traditionally, Cordova-based frameworks used WebView to render the whole application. In contrast, React Native applications run the JS code in a JavaScript VM based on JavaScriptCore. The application uses native JavaScriptCore on iOS and JavaScriptCore libs are bundled on an APK on Android.

In React Native, the communication between Native and JavaScript code is handled by a JavaScript Bridge. The source JS files are compiled into one single bundle file known as entry file. In development mode, the file is bundled on a local server and fetched by the application. For production, the application logic is usually bundled in a single file, usually “index.android.bundle” or “index.ios.bundle“. 

Similarly to Cordova, the bundle file is present in the assets folder and, as also happens with Cordova, we can assume React Native apps as containers that run JS code. This logic is implemented in the expo. Under certain limitations, Expo can run different business logic in a single application. At this moment, it’s fair to assume the entry-file as the core application logic.


Major Sections of Secure Mobile Application

Secure Mobile Application is divided into three sections :

  • Securing application to server connection
  • Securing local storage
  • Advanced integrity checks


1. Securing Application to Server Connection

Usually, smartphone apps communicate with the backend server via APIs. Insecure Communication is highlighted in the OWASP Mobile Top 10 at #3:

Mobile applications frequently do not protect network traffic. They may use SSL/TLS during authentication but not elsewhere. This inconsistency leads to the risk of exposing data and session IDs to interception. The use of transport security does not mean the app has implemented it correctly. To detect basic flaws, observe the phone’s network traffic. More subtle flaws require inspecting the design of the application and the applications configuration. – M3-Insecure Communication.

Starting from iOS 9 and Android Pie, SSL is required by default. We can enable cleartext traffic but it’s not recommended. To secure the connection further, we can pin our server certificates.


SSL Pinning in React Native

Apps are dependent on Certificate Authorities (CA) and Domain Name Servers (DNS) to validate domains for TLS. Unsafe certificates can be installed on a user device, thereby opening the device to a Man-in-the-Middle attack. SSL pinning can be used to mitigate this risk.

We use the fetch API or libraries like axios or frisbee to consume APIs in our React Native applications. However, these libraries don’t have support for SSL pinning. Let’s explore the available plugins:

  • react-native-ssl-pinning: this plugin uses OkHttp3 on Android and AFNetworking on iOS to provide SSL pinning and cookie handling. In this case, we will be using fetch from the library to consume APIs. For this library, we will have to bundle the certificates inside the app. Necessary error handling needs to be implemented in older apps to handle certificate expiry. The app needs to be updated with newer certificates before certificates expire. This library uses promises and supports multi-part form data.
  • react-native-pinch: this plugin is similar to react-native-ssl-pinning. We have to bundle certificates inside the app. This library supports both promises and callbacks.

To use HPKP (Http Public Key Pinning), we can consider these plugins:

  • react-native-cert-pinner: this plugin allows us to use public hashes to pin the server. Unlike the plugins above, we can use fetch and other utilities directly. The pinning occurs before native JS is run. Also, there is no requirement to define hashes in the request itself.
  • react-native-trustkit: this is a wrapper plugin for the iOS Trustkit library. This library is available for iOS only.


2. Securing Local Storage

Normally, we store data inside our application to achieve offline functionality. There are multiple ways to store persistent data in React Native. Async-storage, sqlite, pouchdb and realm are some of the methods to store data.

Insecure storage is highlighted at #2 in the OWASP Mobile Top 10:

Insecure data storage vulnerabilities occur when development teams assume that users or malware will not have access to a mobile device’s filesystem and subsequent sensitive information in data-stores on the device. Filesystems are easily accessible. Organizations should expect a malicious user or malware to inspect sensitive data stores. Usage of poor encryption libraries is to be avoided. Rooting or jailbreaking a mobile device circumvents any encryption protections. When data is not protected properly, specialized tools are all that is needed to view application data. – M2-Insecure Data Storage.

Let’s take a look at some plugins which add a layer of security to our application. Also, we will be exploring some plugins which use native security features Keychain and Keystore Access.


SQLite

SQLite is the most common way to store data. A very popular and open-source extension for SQLite encryption is SQLCipher. Data in SQLCipher is encrypted via 256 bit AES which can’t be read without a key. React Native has two libraries that provide SQLCipher:

  • react-native-sqlcipher-2 : this is a fork of react-native-sqlite-2. We can use pouchdb as an ORM provider with this library, so it’s an additional bonus.
  • react-native-sqlcipher-storage : this is a fork of react-native-sqlite-storage. The library has to be set up manually since it doesn’t seem to support react-native link. Interestingly, the library is based on the Cordova implementation.


Realm

Realm is a nice alternative database provider to React Native Apps. It’s much faster than SQLite and it has support for encryption by default. It uses the AES256 algorithm and the encrypted realm is verified using SHA-2 HMAC hash.


Keychain and Keystore Access

Both iOS and Android have native techniques to store secure data. Keychain services allows developers to store small chunks of data in an encrypted database. On Android, most plugins use the Android keystore system for API 23(Marshmallow) and above. For lower APIs, Facebook’s conceal provides the necessary crypto functions. Another alternative is to store encrypted data in shared preferences.

React Native has three libraries that provide secure storage along with biometric/face authentication:

  • React Native KeyChain: As the name implies, this plugin provides access to keychain/keystore. It uses Keychain (iOS), Keystore (Android 23+), and conceal. There is support for Biometric Auth. This plugin has multiple methods and options for both Android and iOS. However, it only allows the storage of the username & password.
  • React Native Sensitive Info: This plugin is similar to React Native Keychain. It uses Keychain (iOS) and shared preferences (Android) to store data. We can store multiple key-value pairs using this plugin.
  • RN Secure Storage: This plugin is similar to React Native Sensitive Info. It uses Keychain (iOS), Keystore (Android 23+), and Secure Preferences to store data. We can store multiple key-value pairs.


3. Advanced Integrity Checks


JailMonkey and SafetyNet

Rooted and jailbroken devices should be considered insecure by intent. Root privileges allow users to circumvent OS security features, spoof data, analyze algorithms, and access secured storage. As a rule of thumb, the execution of the app on a rooted device should be avoided.

JailMonkey allows React Native applications to detect root or jailbreak. Apart from that, it can detect if mock locations can be set using developer tools.

SafetyNet is an Android-only API for detecting rooted devices and boot loader unlocks. 

react-native-google-safetynet is a wrapper plugin for SafetyNet’s attestation API. It can be used to verify the user’s device.

Additionally, we can use react-native-device-info to check if an app is running in an emulator.


Protecting the Application Logic

Earlier in the article, we mentioned how the application logic in entry-file is available in plain sight. In other words, a third-party can retrieve the code, reverse-engineer sensitive logic, or even tamper with the code to abuse the app (such as unlocking features or violating license agreements).

Protecting the application logic is a recommendation in the OWASP Mobile Top 10. Specifically, the main concerns include code tampering:

Mobile code runs within an environment that is not under the control of the organization producing the code. At the same time, there are plenty of different ways of altering the environment in which that code runs. These changes allow an adversary to tinker with the code and modify it at will. — M8-Code Tampering. “

And reverse engineering:

Generally, most applications are susceptible to reverse engineering due to the inherent nature of code. Most languages used to write apps today are rich in metadata that greatly aides a programmer in debugging the app. This same capability also greatly aides an attacker in understanding how the app works. — M9-Reverse Engineering.”

Let’s highlight two different strategies to address this risk.


Hermes

Facebook introduced Hermes with the react-native 0.60.1 release. Hermes is a new JavaScript Engine optimized for mobile apps. Currently, it is only available with Android and it’s usage is optional. Hermes can be used in the project with react-native 0.60.4 by changing the enableHermes flag in build.gradle file.

Its key benefits are improved start-up time, decreased memory usage, and smaller app size. One of the strategies that Hermes uses to achieve this is precompiling JavaScript to bytecode.

Let’s look at a real example. We assume that our entry-file is the one found below:

const {createDecipheriv, createCipheriv, randomBytes} = require('crypto');
const key = Buffer.from('60adba1cf391d89a3a71c72a615cbba8', 'hex');
const algorithm = 'aes-128-cbc';
const softwareVersion = '2.0';
module.exports.createKey = function(userId, expireDate) {
  const payload = {
    userId,
    expireDate,
    softwareVersion
  };
  const json = Buffer.from(JSON.stringify(payload), 'utf8');
  const iv = randomBytes(16);
  const cipher = createCipheriv(algorithm, key, iv);
  let encoded = cipher.update(json);
  encoded = Buffer.concat([encoded, cipher.final()]);
  const joined = iv.toString('hex') + ';' + encoded.toString('hex');
  return Buffer.from(joined, 'utf8').toString('base64');
}
module.exports.validateLicense = function(license, userId) {
  const licenseFields = Buffer.from(license, 'base64').toString('utf8');
  const fields = licenseFields.split(';');
  const iv = Buffer.from(fields[0], 'hex');
  const data = Buffer.from(fields[1], 'hex');
  const decipher = createDecipheriv(algorithm, key, iv);
  let decoded = decipher.update(data);
  decoded = Buffer.concat([decoded, decipher.final()]);
  const result = JSON.parse(decoded);
  if (result.userId != userId) {
    throw new Error('Wrong user');
  }
  if (new Date(result.expireDate) < new Date()) {
    throw new Error('Expired license');
  }
  if (result.softwareVersion != softwareVersion) {
    throw new Error('This license is not valid for this program version');
  }
  return result;
}

After Hermes compiles this file, the resulting bytecode can easily be decompiled using hbcdump and, among the decompiled code, we find some easy to read code look like:

s0[ASCII, 0..-1]: 
s1[ASCII, 0..2]: 2.0
s2[ASCII, 3..34]: 60adba1cf391d89a3a71c72a615cbba8
s3[ASCII, 35..35]: ;
s4[ASCII, 36..50]: Expired license
s5[ASCII, 71..120]: This license is not valid for this program version
s6[ASCII, 121..130]: Wrong user
s7[ASCII, 133..143]: aes-128-cbc
s8[ASCII, 143..148]: crypto
s9[ASCII, 154..159]: global
s10[ASCII, 160..165]: base64
s11[ASCII, 166..168]: hex
s12[ASCII, 177..180]: utf8
i13[ASCII, 50..56] #C765D706: exports
i14[ASCII, 56..70] #FF849242: softwareVersion
i15[ASCII, 127..132] #6FE51CD4: userId
i16[ASCII, 147..154] #1E019520: toString
i17[ASCII, 167..176] #68A06D42: expireDate
i18[ASCII, 173..176] #CD347266: Date
i19[ASCII, 181..186] #5AA7C487: Buffer
i20[ASCII, 186..196] #FD81EB01: randomBytes
i21[ASCII, 196..200] #0EC469F8: split
i22[ASCII, 201..205] #9102A3D0: Error
i23[ASCII, 205..211] #EB75CA32: require
i24[ASCII, 212..215] #971CE5C7: JSON
i25[ASCII, 216..221] #CB8DFA65: concat
i26[ASCII, 222..235] #96C7181F: createCipheriv
i27[ASCII, 235..249] #D60B6B51: validateLicense
i28[ASCII, 250..265] #723D6A80: createDecipheriv
i29[ASCII, 266..274] #01D3AE7D: createKey
i30[ASCII, 275..279] #47993A63: final
i31[ASCII, 280..283] #EAF03666: from
i32[ASCII, 283..288] #2A322C6E: module
i33[ASCII, 289..293] #958EDB02: parse
i34[ASCII, 294..302] #807C5F3D: prototype
i35[ASCII, 303..311] #8D1543BD: stringify
i36[ASCII, 312..317] #60396F4B: update

Function<global>0(1 params, 15 registers, 4 symbols):
Offset in debug table: src 0x0, vars 0x0
license.js[1:1]
    CreateEnvironment r0
    GetGlobalObject   r1
    TryGetById        r4, r1, 1, "require"
    LoadConstUndefined r3
    LoadConstString   r2, "crypto"
    Call2             r2, r4, r3, r2
    GetByIdShort      r3, r2, 2, "createDecipheriv"
    StoreToEnvironment r0, 0, r3
    GetByIdShort      r3, r2, 3, "createCipheriv"
    StoreToEnvironment r0, 1, r3
    GetByIdShort      r2, r2, 4, "randomBytes"
    StoreToEnvironment r0, 2, r2
    TryGetById        r5, r1, 5, "Buffer"
    GetByIdShort      r4, r5, 6, "from"
    LoadConstString   r3, "60adba1cf391d89a3"...
    LoadConstString   r2, "hex"
    Call3             r2, r4, r5, r3, r2
    StoreToEnvironment r0, 3, r2
    TryGetById        r2, r1, 7, "module"
    GetByIdShort      r3, r2, 8, "exports"
    CreateClosure     r2, r0, 1
    PutById           r3, r2, 1, "createKey"
    TryGetById        r1, r1, 7, "module"
    GetByIdShort      r1, r1, 8, "exports"
    CreateClosure     r0, r0, 2
    PutById           r1, r0, 2, "validateLicense"
    Ret               r0

So, while Hermes introduces a certain degree of complexity to the entry-file code, it doesn’t actually conceal the code nor do anything to prevent code tampering, which means that it won’t stop an attacker ⁠— let’s not forget that this is not even the purpose of Hermes.

And this leads us to an approach that obfuscates React Native’s JavaScript source code to effectively mitigate the risk of code tampering and reverse engineering: Jscrambler.


Jscrambler

Jscrambler provides a series of layers to protect JavaScript. Unlike most tools that only include (basic) obfuscation, Jscrambler provides three security layers:

  • Polymorphic JavaScript & HTML5 obfuscation
  • Code locks (domain, OS, browser, time frame)
  • Self-defending (anti-tampering & anti-debugging)

By protecting the source code of React Native apps with Jscrambler, the resulting code is highly obfuscated, as can be observed below:

On top of this obfuscation, there’s a Self-Defending layer that provides anti-debugging and anti-tampering capabilities and enables setting countermeasures like breaking the application, deleting cookies, or destroying the attacker’s environment.

That’s all about in this article.


Conclusion

In this article, We learned how to secure Mobile applications in React Native. We discussed about Mobile application security related areas and techniques for React Native . It provides an overview of techniques to help to secure the React Native application. It’s then crucial to create a threat model , and depending on the application’s use case, employ the required measures to ensure that the application is properly secured.

Thanks for reading ! I hope you enjoyed and learned about the Mobile Application Security concepts in React Native. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Exit mobile version