react-native-ssl-pinning-proposal
react-native-ssl-pinning-proposal copied to clipboard
My proposal of SSL pinning for React Native
react-native-ssl-pinning-proposal
Background
SSL pinning is a technology for client applications to ensure security for server transmission. Instead of original SSL CA validation process, SSL pinned client only accepts server certificate which provide by the author.
i.e. Originally, a client device can redirect to fake https://www.facebook.com/ if that client installed a malicious Root Cerficate as well as its DNS record was polluted. By SSL pinning, client application can prevent this due to the Root Cerficate is different to pinned certificate.
For me, there are two advantages of SSL pinning:
- Prevent MITM attack just as mentioned above.
- Protect your API. Originally, it is quite easy to know how the app call its API server by the technology such as mitmproxy. It will increase the difficulty by SSL pinning. Please note that is not 100% protection. Attackers can still inspect the traffic on rooted or jailbreaked devices.
Prior art
For some iOS/Android network libraries, SSL pinning are already implemented.
For React Native, the Android version uses okhttp, so we can adopt it easily. For iOS, we should do it handy.
What to be pinned
There are many variations about what information to be pinned, from the whole certificate, public key and SubjectPublicKeyInfo(SPKI).
There are also some variations to pinned format, from binary file, base64 encoded, or hashed string.
For me, I prefer to pin the SPKI and in sha1 hashed format, as aligned with okhttp and Google Chrome.
Where to store the pinned information
Originally for native mobile app development, we used to setup pinned information in source code as the okhttp sample
client = new OkHttpClient.Builder()
.certificatePinner(new CertificatePinner.Builder()
.add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
.add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
.add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
.add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
.build())
.build();
However, for React Native, I prefer to store the pinned information in JavaScript. The advantage is for developr can update the pinned information easier. However, storing pinned information in JavaScript will add vulnerablity that attackers can still give you malicious RN bundle file. That is why I am thinking to introduce digital signature for bundle file.
Action items
There are two parts of action items:
Bundle file Digital Signature
- Modify signedsource in RN packager that accepted a given RSA private key file. (Currently the signedsource only do MD5 checksum to do integrity check).
- Modify
react-native bundle
to accept given RSA pem file to do sign. - [iOS] Add new interface to
RCTBridge
that accept RSA public key, theninitWithBundleURL:...
inRCTRootView
can accept a public key for bundle authorize verification. - [iOS] Port signedsource.verify_signature() logic but allow verify by given public key to Objective C.
- [Android] Add new interface to
ReactInstanceManager
that accept RSA public for bundle authorize verification. - [Android] Port signedsource.verify_signature() logic but allow verify by given public key to Java.
SSL Pinning
- Add new interface to
fetch
andXMLHttpRequest
that accepts{domain: SPKI_sha1_hashed_string}
pair for pinning. - [iOS] Modify
RCTNetworking
and foundmentalNSURLSession
to accept SPKI sha1 hashed string and do verification when connecting to target domain. - [Android] Modify
RCTNetworking
and pass given pinning information to okhttp