frw / react-native-ssl-public-key-pinning

Simple and secure SSL public key pinning for React Native. No native configuration needed, set up in <5 minutes.
MIT License
163 stars 13 forks source link

SSL Pinning Not Working Correctly on android and iOS #365

Closed ShulmanK closed 1 month ago

ShulmanK commented 1 month ago

I have implemented this library to restrict users from making API requests when incorrect pins are provided.

It appears that only the initializeSslPinning function is working correctly. This function checks the pins and throws an error if they are incorrect. However, despite incorrect or missing pins, API calls are still successful on iOS and android.

Steps to Reproduce:

1.  Implement SSL pinning using the provided library.
2.  Initialize SSL pinning with incorrect pins.
3.  Make an API request.

Expected Behavior:

API requests should fail when incorrect pins are provided.

Actual Behavior:

API requests succeed even when incorrect or missing pins are provided.

import React, { useEffect } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { initializeSslPinning, addSslPinningErrorListener } from 'react-native-ssl-public-key-pinning';

const App = () => {
  useEffect(() => {
    const setupSslPinning = async () => {
      try {
        await initializeSslPinning({
          'google.com': {
            includeSubdomains: true,
            publicKeyHashes: [
              'CLOmM1/OXvSPjw5UOYbAf9GKOxImEp9hhku9W90fHMk=',
              'hxqRlPTu1bMS/0DITB1SSu0vd4u/8l8TjPgfaAp63Gc=',
              'Vfd95BwDeSQo+NUYxVEEIlvkOlWY2SalKK1lPhzOx78=',
              'QXnt2YHvdHR3tJYmQIr0Paosp6t/nggsEGD4QJZ3Q0g=',
              'mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=',
            ],
          },
        });
        console.log('SSL pinning initialized successfully');
      } catch (error) {
        console.error('Failed to initialize SSL pinning', error);
      }
    };

    setupSslPinning();

    const callApi = async () => {
      try {
        const response = await fetch('https://www.google.com');
        console.log('Network request succeeded');
      } catch (error) {
        console.log('Network request failed', error);
      }
    };

    callApi();
  }, []);

  useEffect(() => {
    const subscription = addSslPinningErrorListener(error => {
      console.log('SSL Pinning Error:', error.serverHostname);
      console.log(error.message);
    });

    return () => subscription.remove();
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.text}>SSL Pinning Example</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  text: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
});

export default App;
frw commented 1 month ago

Hi @ShulmanK,

What version of React Native are you using with this library? Are you using Expo in your setup?

If you can please provide a repository with a minimal reproducible example.

You can check out the example repository for a working setup: https://github.com/frw/react-native-ssl-public-key-pinning/tree/main/example

ShulmanK commented 1 month ago

Hi @frw , Thank you for your answer. I tried to install new react-native project (without expo) using latest version 0.74.3 and reproduce the code from your example and app requests are still doable with incorrect pin.

This is a link to repository: https://github.com/ShulmanK/ssl-pinning-example

https://github.com/user-attachments/assets/9b1fbd80-9e93-4937-9b0b-a2568e19f151

I attached some screencast. As you can see fetch request to the google.com are successful even with incorrect pin. Is there a chance I'm doing smth wrong. I would happy if you can point me it out.

Also my package.json

{
  "name": "AwesomeProject",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "lint": "eslint .",
    "start": "react-native start",
    "test": "jest"
  },
  "dependencies": {
    "react": "18.2.0",
    "react-native": "0.74.3",
    "react-native-ssl-public-key-pinning": "^1.2.4"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@babel/preset-env": "^7.20.0",
    "@babel/runtime": "^7.20.0",
    "@react-native/babel-preset": "0.74.85",
    "@react-native/eslint-config": "0.74.85",
    "@react-native/metro-config": "0.74.85",
    "@react-native/typescript-config": "0.74.85",
    "@types/react": "^18.2.6",
    "@types/react-test-renderer": "^18.0.0",
    "babel-jest": "^29.6.3",
    "eslint": "^8.19.0",
    "jest": "^29.6.3",
    "prettier": "2.8.8",
    "react-test-renderer": "18.2.0",
    "typescript": "5.0.4"
  },
  "engines": {
    "node": ">=18"
  },
  "packageManager": "yarn@3.6.4"
}

And Pins I used:

const GTS_HASHES = [
  'CLOmM1/OXvSPjw5UOYbAf9GKOxImEp9hhku9W90fHMk=wrongpart', // GlobalSign R4
  // 'hxqRlPTu1bMS/0DITB1SSu0vd4u/8l8TjPgfaAp63Gc=', // GTS Root R1
  // 'Vfd95BwDeSQo+NUYxVEEIlvkOlWY2SalKK1lPhzOx78=', // GTS Root R2
  // 'QXnt2YHvdHR3tJYmQIr0Paosp6t/nggsEGD4QJZ3Q0g=', // GTS Root R3
  // 'mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=', // GTS Root R4
].join('\n');

I would appreiate if you can take a look. Thank you in advance!

frw commented 1 month ago

Hey @ShulmanK

When you click on "Initialize Pinning", there is an error message which complains about invalid SHA256 hashes (they are invalid because you appended some stuff at the end of the hash). This means that the pinning hasn't actually succeeded. You should be using actual SHA256 hashes, but the hashes shouldn't match your certificate's public key hash if you want to see the fetch fail. For examples of failing hashes, you can check the FAQ (here's an example of one: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=. Note that this is a valid SHA256 hash but will probably not match the public key hash of any server's certificate)

gkasireddy202 commented 1 month ago

I added my host and public key to the index.js page. Getting a network error even though I added the correct hostname and public key. I tested it on an Android 14 device. react-native:0.68.7 react-native-ssl-public-key-pinning: 1.1.3

I have a small question: Is the hostname and base URL we are using should be the same?

example: hostname: myhost.com Base URL: www.myhost.com

await initializeSslPinning({ 'host': { includeSubdomains: true, publicKeyHashes: [ 'publickey', ], }, });

frw commented 1 month ago

@gkasireddy202,

Please provide a repository with a minimal reproducible example, as well as any relevant error messages you get. I am unable to tell why your particular setup does not work without them.

You should be able to use myhost.com and set includeSubdomains: true to pin all subdomains of myhost.com, including www.myhost.com

Do try to use the latest version of the library as well.

gkasireddy202 commented 1 month ago

@frw - Thanks for your reply. I am facing with hostname:myhost.com and base URL: www.cloud.myhost.com. I resolved the issue by adding the hostname:www.cloud.myhost.com.

frw commented 1 month ago

Closing for inactivity. Please reopen if you're still having issues.