Implementing SSL Pinning in React Native Apps: A Native Approach for iOS and Android
Introduction:
In the ever-evolving landscape of mobile app development, security is a top priority. Secure Sockets Layer (SSL) Pinning is a crucial mechanism that enhances the security of your React Native apps by preventing man-in-the-middle attacks. This blog post will guide you through the process of implementing SSL pinning in React Native apps, with a focus on native implementation for both iOS and Android platforms.
Before we jump into native code, we need to get the certificates from the domain we will be pinning, using the link below to show the list of certificates on the domain.
Understanding SSL Pinning:
SSL pinning involves associating a specific SSL certificate with your mobile app, ensuring that only connections with the pinned certificate are trusted. This helps protect your app from potential threats like eavesdropping and interception of sensitive data during communication between the app and a server.
Prerequisites:
Before diving into the implementation, make sure you have the following:
- A React Native project was set up with the necessary dependencies.
- SSL certificates for your server.
iOS Implementation:
SSL pinning on iOS
To implement SSL pinning on iOS, we will use a native library called Trustkit. We can install it through CocoaPods.
Open your podfile and add the following line:
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform: :ios, '12.4'
install! 'cocoapods', :deterministic_uuids => false
target 'sslPinning' do
config = use_native_modules!
+ pod 'TrustKit' /// <<=====add this
Flags change depending on the env values.
flags = get_default_flags()
...
In your iOS project, open the AppDelegate.m
file and import the necessary libraries:
Next, go to your /ios directory and run pod install so the library is correctly linked to your project.
Once we have Trustkit installed, the SSL pinning setup is next. For that, open the AppDelegate.mm file using your favorite editor—we recommend Xcode for dealing with iOS platform files—and call TrustKit’s initSharedInstanceWithConfiguration method with a trustKitConfig parameter like this:
In your iOS project, open the AppDelegate.m
file and import the necessary libraries:
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
+#import <TrustKit/TrustKit.h> <<add this in Your code
#import <React/RCTAppSetupUtils.h>
...
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
<<add this in Your code from
{
+ NSDictionary *trustKitConfig =
+ @{
+ kTSKSwizzleNetworkDelegates: @YES,
+ kTSKPinnedDomains: @{
+ @"sslpinning.com" : @{
+ kTSKIncludeSubdomains: @YES,
+ kTSKEnforcePinning: @YES,
+ kTSKDisableDefaultReportUri: @YES,
+ kTSKPublicKeyHashes : @[
+ @"<Primary SHA256 key>",
+ @"<Backup SHA256 key>",
+ ],
+ },
+ }};
+ [TrustKit initSharedInstanceWithConfiguration:trustKitConfig];
<<add this in Your code here
RCTAppSetupPrepareApp(application);
Replace "your_certificate"
with the actual name of your certificate file.
Android Implementation:
Under the hood, React Native Android uses OkHttp, a library for network calls with SSL pinning support out of the box. It makes SSL pinning configuration on Android even simpler than on iOS.
You need to create a new Java file inside android/app/src/main/java/com/<your_domain>. I’ve called it SSLPinningFactory.java, but this is entirely up to you.
package com.sslpinning; // your domain here
import com.facebook.react.modules.network.OkHttpClientFactory;
import com.facebook.react.modules.network.OkHttpClientProvider;
import okhttp3.CertificatePinner;
import okhttp3.OkHttpClient;
public class SSLPinningFactory implements OkHttpClientFactory {
private static String hostname = "sslpinning.com";
public OkHttpClient createNewNetworkModuleClient() {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/<your SHA256 key>")
.build();
OkHttpClient.Builder clientBuilder = OkHttpClientProvider.createClientBuilder();
return clientBuilder.certificatePinner(certificatePinner).build();
}
}
Replace "your_domain.com"
with your actual domain and"sha256/your_cert_hash"
with the SHA-256 hash of your certificate. You can obtain the hash using tools like OpenSSL.
The next step will be OkHttpClient setup with our newly created SSLPinningFactory class. We can do so in the app’s MainApplication.java. Import com.facebook.react.modules.network.OkHttpClientProvider and call its setOkHttpClientFactory method with a new instance of our SSLPinningFactory as below:
...
+ add this >>> import com.facebook.react.modules.network.OkHttpClientProvider;
...
public class MainApplication extends Application implements ReactApplication {
...
@Override
public void onCreate() {
super.onCreate();
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
+ add this >>> OkHttpClientProvider.setOkHttpClientFactory(new SSLPinningFactory());
}
...
}
Conclusion:
By implementing SSL pinning natively in your React Native app for both iOS and Android, you enhance the security of your app and protect it from potential security threats. Stay vigilant about keeping your SSL certificates updated, and regularly review your security practices to ensure a robust defense against malicious activities.