Skip to main content

Premiers pas avec la sécurité de React Native

Écrit par

Kingsley Ubah

blog banner node js

7 avril 2022

0 minutes de lecture

React offre un moyen simple et intuitif de créer des interfaces utilisateur interactives. Il vous permet de concevoir des applications complexes à partir de petits blocs de code indépendants appelés composants. React Native est une extension de React qui permet aux développeurs de combiner les techniques utilisées dans les technologies web, comme JavaScript, avec React pour créer des applications mobiles multiplateformes.

Les développeurs peuvent ainsi écrire le code une seule fois pour plusieurs plateformes, ce qui accélère le développement. React Native utilise un DOM virtuel pour exécuter les composants et le code des applications sur les appareils fonctionnant sous Android, Android TV, iOS, macOS, tvOS, web, Windows et UWP.

Cette adoption est une excellente nouvelle pour React, car les technologies mobiles se développent et évoluent à un rythme sans précédent. D’ici 2025, le nombre d’utilisateurs mobiles dans le monde devrait atteindre 7,49 milliards. Les applications mobiles sont désormais omniprésentes et deviennent de plus en plus sophistiquées dans leur manière de partager, de traiter et de sécuriser les données des utilisateurs.

En tant que développeur d’une application destinée aux utilisateurs, vous êtes responsable de la sécurité des informations des utilisateurs de votre application et des autres informations sensibles du serveur. Vous devez donc veiller à ce que chacun n’accède qu’aux informations pour lesquelles il est autorisé et à ce que les informations des utilisateurs stockées dans la base de données restent sécurisées.

Pour protéger les informations des utilisateurs finaux, les applications ne doivent en aucun cas exposer de clés privées, d’informations de base de données, de fichiers sensibles ni de parties de page révélant la structure ou le fonctionnement du back-end. Explorons donc certains défis de sécurité rencontrés par les développeurs React Native lors de la création d’applications mobiles, comme le transfert sécurisé des données, les protocoles d’authentification et les vulnérabilités des dépendances. Nous verrons également comment résoudre chacun de ces problèmes.

Transfert sécurisé des données

Il est difficile de trouver une application mobile qui n’ait pas besoin de données provenant d’un serveur distant. Par défaut, React Native utilise uniquement HTTPS pour les appels d’API et les transferts de données.

Même s’il est possible de remplacer ce paramètre par défaut, l’utilisation d’un protocole autre que HTTPS entraînera des problèmes avec votre fournisseur lors de la publication sur la boutique d’applications. Plus grave encore, elle peut exposer votre application mobile à différentes formes de fuite de données.

Vous pouvez épingler manuellement le ou les certificats de confiance de l’hôte distant dans une application mobile pour empêcher les attaques de type « homme du milieu » (MITM) et renforcer la sécurité de l’application. Plusieurs bibliothèques open source d’épinglage de certificats pour React Native conviennent à cet usage, comme react-native-ssl-pinning de MaxToyberman ou react-native-cert-pinner d’Approov.

Épingler des certificats avec la bibliothèque React Native SSL pinning

React Native SSL pinning convertit les certificats en fichiers .cer que vous pouvez ajouter aux ressources de votre application mobile, comme vous le feriez pour un fichier image ou une police personnalisée.

L’outil remplace fetch, que nous pouvons utiliser pour effectuer nos requêtes HTTP.

Pour épingler un certificat, utilisez ce code :

import { fetch } from 'react-native-ssl-pinning'

const response = await fetch(url, {
  method: 'POST',
  body: body,
  timeoutInterval: milliseconds,
  headers: {
    Accept: "application/json; charset=utf-8",
  },
  sslPinning: {
    certs: ["cert1", "cert2"] // your certificates name (without extension), e.g. cert1.cer
  }
})

Pour épingler une clé publique, utilisez ce code :

import { fetch } from 'react-native-ssl-pinning'

const response = await fetch(url, {
  method: 'POST',
  body: body,
  timeoutInterval: milliseconds,
  headers: {
    Accept: "application/json; charset=utf-8",
  },
  pkPinning: true,
  sslPinning: {
    certs: ["sha256//r8udi/Ymw34stt44fZybMWq8YnFnIWXCqeHsTDRqy8=",
        "sha256/QShfdvfff6fja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=",        
      ] // public keys
  }
})

La bibliothèque s’appuie sur deux modules natifs : AFNetworking pour iOS et okhttp3 pour Android. Le module AFNetworking utilise automatiquement les fichiers .cer dans les ressources. OkHttp3 utilise la propriété JavaScript sslPinning pour établir la correspondance avec .cer.

Épingler des certificats avec react-native-cert-pinner

react-native-cert-pinner est une autre bibliothèque qui vous permet d’épingler des certificats dans votre application mobile. Les épingles peuvent être le hachage SHA256 Base64 de la clé publique du certificat ou le hachage SHA1 Base64 du certificat.

La bibliothèque fournit un utilitaire, pinset, qui génère un fichier info.plist pour iOS ou un fichier GeneratedCertificatePinner.java pour Android. Le code natif établit ensuite un lien vers ce fichier.

En interne, la bibliothèque utilise TrustKit sur iOS et okhttp3 sur Android.

Pour effectuer un épinglage avec react-native-cert-pinner, commencez par générer une configuration de départ avec cette commande :

$ npx pinset init

Après l’initialisation, déterminez les URL à épingler ainsi que le hachage de la clé publique de chaque certificat.

Saisissez ensuite les hachages des clés publiques souhaitées pour votre domaine dans le fichier pinset.json :

{
  "domains": {
    "*.yourdomainhere.com": {
      "pins": [
        "sha256/0000000000000000000000000000000000000000000",
        "sha256/1111111111111111111111111111111111111111111"
      ]
    },
   // other domains
  }

Remarque : les domaines commençant par un astérisque (*) incluent tous les sous-domaines.

Générez maintenant les sources du projet natif avec la commande suivante :

$ npx pinset gen

Cette action doit générer les fichiers correspondants :

Reading config file './pinset.json'.
Updating java file './android/app/src/main/java/com/snyk/reactnative/GeneratedCertificatePinner.java'.
Updating plist file './ios/example/info.plist'.

Nous vous recommandons d’utiliser une bibliothèque qui permet d’épingler la clé publique du certificat, car une épingle créée à partir d’un certificat cessera de fonctionner à son expiration. Les deux bibliothèques présentées dans cette section permettent d’épingler des clés publiques.

En règle générale, il est recommandé de n’épingler des certificats dans une application mobile que si vous souhaitez avoir une confiance raisonnable dans l’identité de l’hôte distant.

Protocoles d’authentification

L’authentification permet d’établir la confiance en identifiant l’utilisateur ou le système à l’origine d’une requête réseau. Cette identification aide le serveur à déterminer s’il doit autoriser l’utilisateur à accéder à une ressource donnée.

La plupart des applications mobiles doivent afficher à l’utilisateur une forme de données personnalisées. Pensez au profil d’un utilisateur dans l’application Facebook ou à la liste de contacts de WhatsApp. Le système d’authentification aide le serveur à identifier l’utilisateur afin de lui fournir les données correspondantes.

Une méthode courante pour configurer l’authentification des utilisateurs dans React Native consiste à créer différents groupes d’écrans. Les développeurs le font souvent à l’aide d’une bibliothèque de routage comme Stack Navigator.

Un groupe gère les écrans susceptibles d’être consultés par les utilisateurs non authentifiés. Il peut s’agir des écrans de connexion, d’inscription et de réinitialisation du mot de passe. L’application utilise ces écrans pour authentifier l’utilisateur.

Un deuxième groupe est destiné aux utilisateurs authentifiés. Cette pile comprend le tableau de bord, les paramètres, le compte, le fil d’actualité et tout autre écran lié au contenu de l’utilisateur.

Cet exemple de code répartit les écrans en deux groupes : AppStack pour les utilisateurs authentifiés et AuthStack pour les utilisateurs non authentifiés :

import {createStackNavigator} from '@react-navigation/stack';
const Stack = createStackNavigator();

const AppStack = () => {
  return (
    <Stack.Navigator>
      <Stack.Screen name="Home Screen" component={HomeScreen} />
      <Stack.Screen name="Notification Screen" component={NotificationsScreen} />
      <Stack.Screen name="Profile Screen" component={ProfileScreen} />
      <Stack.Screen name="Setting Screen" component={SettingsScreen} />
    </Stack.Navigator>
  );
};

const AuthStack = () => {
  return (
    <Stack.Navigator>
      <Stack.Screen name="Sign In Screen" component={SignInScreen} />
      <Stack.Screen name="Register Screen" component={RegisterScreen} />
      <Stack.Screen name="Reset Password Screen" component={ResetPasScreen} />
    </Stack.Navigator>
  );
};

Après avoir créé les deux groupes d’écrans, créez un composant Router chargé de choisir la pile à afficher en fonction de l’état d’authentification de l’utilisateur.

Utilisez ensuite les deux piles dans le NavigationContainer. Un composant de react-navigation englobe toute la navigation entre les routes dans React Native.

import {NavigationContainer} from '@react-navigation/native';

import {AppStack} from './AppStack';
import {AuthStack} from './AuthStack';

export const Router = () => {
  return (
    <NavigationContainer>
      {authData ? <AppStack /> : <AuthStack />}
    </NavigationContainer>
  );
};

authData représente les données fournies par l’utilisateur sur l’écran d’authentification, par exemple SignInScreen. Ce code authentifie l’utilisateur :

import React, {createContext, useState, useContext} from 'react';
import {AuthData, authService} from '../services/authService';

const AuthContext = createContext<AuthContextData>({} as AuthContextData);

const AuthProvider: React.FC = ({children}) => {
  const [authData, setAuthData] = useState<AuthData>();

  const [loading, setLoading] = useState(true);

  const signIn = async () => {
    const _authData = await authService.signIn(
      'test@email.com',
      '12345678',
    );

    setAuthData(_authData);
  };

  const signOut = async () => {
    setAuthData(undefined);
  };

  return (
    <AuthContext.Provider value={{authData, loading, signIn, signOut}}>
      {children}
    </AuthContext.Provider>
  );
};

La fonction signIn se connecte à l’API et met à jour authData en fonction de la réponse. Vous pouvez y stocker les données du profil utilisateur provenant de l’API. La fonction signOut efface les données. Dans certains cas, elle peut se connecter à l’API pour invalider un jeton.

Un état de chargement est nécessaire, car le processus d’authentification est asynchrone et prend du temps. Vous pouvez surveiller cet état pour afficher une animation de chargement dans l’interface utilisateur (UI) de votre application pendant que le serveur traite la requête.

Enfin, veillez à ce que tous les composants puissent accéder au Context en l’encapsulant dans AuthContext.Provider.

Le protocole OAuth 2.0

Les applications mobiles React Native utilisent couramment le protocole d’authentification OAuth 2.0 pour authentifier les utilisateurs. OAuth est très apprécié pour les applications web et mobiles en raison de ses exigences strictes en matière de sécurité.

Le flux OAuth fait intervenir quatre acteurs :

  • Propriétaire de la ressource : détient les données du serveur de ressources. Par exemple, un utilisateur Facebook est le propriétaire des données de son profil Facebook.

  • Application cliente : l’application qui souhaite accéder à ces données (par exemple, l’application mobile Facebook).

  • Serveur de ressources : l’API qui stocke les données auxquelles l’application mobile souhaite accéder (par exemple, l’API Facebook).

  • Serveur d’autorisation : le moteur principal d’OAuth.

OAuth invite les utilisateurs à s’authentifier via un service tiers, comme Google, Facebook ou GitHub. Une fois l’authentification terminée, le service tiers redirige l’utilisateur vers l’application avec un code de vérification. L’application peut échanger ce code contre un jeton Web JSON (JWT) ou tout autre type de jeton.

Ces redirections sont sécurisées pour les applications web, car les URL sont garanties uniques sur le web. Cependant, les applications mobiles ne disposent pas d’une méthode centralisée pour enregistrer les schémas d’URL.

Pour répondre à ce problème de sécurité, nous devons ajouter une vérification sous la forme de PKCE (prononcé « Pixie »), une extension de la spécification OAuth 2.0. PKCE utilise l’algorithme de hachage cryptographique SHA 256 pour vérifier que les requêtes d’authentification et d’échange de jetons proviennent du même client.

Cette approche garantit que seule l’application responsable du flux d’autorisation peut échanger le code de vérification contre un JWT.

Pour implémenter simplement et en toute sécurité OAuth natif dans React Native, vous pouvez utiliser un package comme react-native-app-auth. Il sert de passerelle React Native pour les SDK AppAuth pour iOS et AppAuth pour Android, qui communiquent avec les fournisseurs OAuth 2.0 et OpenID Connect.

Ce package prend en charge PKCE ainsi que de nombreux fournisseurs OpenID et OAuth 2.0, notamment Google, Okta, GitHub, Coinbase et FusionAuth.

Rechercher les vulnérabilités des dépendances

Les développeurs ne créent pas seuls des applications natives complètes avec React Native. Nous utilisons souvent des outils tiers pour résoudre des problèmes et améliorer notre productivité.

Une application contient plusieurs dépendances tierces qui contribuent à la rendre complète et fonctionnelle. Le problème, c’est que ces dépendances peuvent introduire des vulnérabilités dans notre application, même si notre propre code est sécurisé.

Il existe plusieurs façons de commencer à utiliser Snyk pour rechercher les vulnérabilités dans les packages open source. Prenons l’exemple d’Eigen, une application de dessin React Native open source très populaire : Eigen.

La première méthode consiste à utiliser l’interface de ligne de commande Snyk : installez l’outil CLI et créez gratuitement un compte Snyk. Une fois votre compte créé et l’outil installé, exécutez snyk auth pour connecter la CLI à votre compte.

Clonez le dépôt dans votre environnement de développement, puis exécutez snyk test dans le répertoire racine du projet cloné. Cette commande analyse le manifeste de packages à la racine et recherche des problèmes dans les versions des packages.

Astuce : vous pouvez également exécuter snyk monitor pour être alerté des problèmes détectés dans le projet après l’analyse initiale.

Sortie du terminal d’une analyse des dépendances signalant 32 problèmes et 114 chemins vulnérables dans un projet React Native.

Dans notre exemple, plusieurs problèmes potentiels ont été détectés dans le manifeste de packages de l’application React Native. Il est toujours utile de pouvoir examiner les vulnérabilités détectées pour en savoir plus.

Dans la liste, Hermes-engine 0.5.1 présente une vulnérabilité connue susceptible d’entraîner une attaque par script intersite (XSS). Vous pouvez y remédier en passant à une version supérieure à 0.7.2, dans laquelle cette vulnérabilité a été corrigée.

Avis de sécurité concernant une vulnérabilité de type Cross-Site Scripting (XSS) touchant les versions de hermes-engine antérieures à la version 0.7.2, avec un score de gravité moyen de 6,1.

Une autre façon d’analyser des projets React Native consiste à lancer une analyse directement depuis un dépôt, en connectant votre compte Snyk au dépôt de code de votre choix. Ainsi, Snyk peut effectuer des analyses continues pour détecter les problèmes dans les projets auxquels contribuent plusieurs personnes, car chaque commit ou fusion déclenche une vérification du projet.

Pour connecter votre dépôt, créez un compte Snyk. Dans le tableau de bord Snyk, cliquez ensuite sur la plateforme de dépôt où votre code est hébergé. Si la plateforme de votre choix ne figure pas dans le menu rapide, cliquez sur Autre et recherchez-la parmi les intégrations de plateforme Snyk.

Menu d’ajout d’un projet affichant les options GitHub, Cloud Foundry, Bitbucket Cloud, CLI, surveillance de dépôts GitHub publics et Autre

Suivez les étapes de connexion. Sur la page de connexion du dépôt, choisissez celui que vous souhaitez analyser.

Écran de sélection des dépôts GitHub de Snyk, avec « eigen » dans la recherche et le dépôt « eigen » sélectionné sous developersteve.

Cliquez sur le bouton Ajouter les dépôts sélectionnés et attendez la fin de l’analyse. Vous pourrez alors consulter les éléments analysés et les résultats.

Tableau de bord de sécurité du code répertoriant les fichiers du projet et leurs évaluations en matière de couverture, de vulnérabilités, de maintenabilité et de vérification des licences.

Comme pour l’analyse avec la CLI Snyk, vous pouvez examiner en détail les vulnérabilités détectées et lancer un correctif automatique directement dans le dépôt.

Rapport de vulnérabilité de sécurité pour ansi-regex présentant une vulnérabilité de gravité élevée de type déni de service par expression régulière (ReDoS) et les versions concernées.

Vous pouvez également intégrer Snyk à l’éditeur de code de votre choix pour analyser automatiquement vos dépendances à la recherche de vulnérabilités pendant le développement d’une application. Par exemple, l’extension Snyk pour Visual Studio Code détecte les vulnérabilités potentielles non seulement dans le manifeste des packages, mais aussi à l’endroit où le package est appelé dans le code.

Pour conclure

À mesure que les applications mobiles exploitent des données toujours plus complexes, il est plus que jamais essentiel de protéger efficacement les informations des utilisateurs et les utilisateurs finaux. Comme les applications Web, les applications React Native sont confrontées aux mêmes types de défis de sécurité, notamment le transfert sécurisé des données, les protocoles d’authentification et les vulnérabilités des dépendances.

Consultez cette liste de bonnes pratiques de sécurité pour React afin d’aider votre équipe et vous-même à détecter et corriger les problèmes de sécurité dans vos applications React.

Lancez-vous dans les compétitions Capture The Flag

Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.

Kingsley Ubah est un développeur Web full-stack expérimenté et un rédacteur technique.

Lire la suite

blog feature ai lilac
Blog

Transformez votre backlog de vulnérabilités en pull requests grâce à la remédiation agentique

Evo Remediation Agent est désormais disponible en aperçu ouvert et apporte la résolution automatisée des dépendances à Evo Agentic AppSec. Les équipes AppSec peuvent désormais lancer des campagnes de remédiation continues

AI Background min crop
Blog

L’IA fait surgir tous les risques que vous avez acceptés : sept enseignements du panel de sécurité de l’IA sans filtre

L’IA rend les attaques moins coûteuses et plus rapides, exposant des risques déjà présents dans les organisations. Quatre responsables de la sécurité expliquent pourquoi une défense à la vitesse des machines et une remédiation accélérée sont désormais indispensables.

blog feature ai violet
Blog

VulnBump : les agents IA peuvent-ils corriger les dépendances vulnérables sans casser la build ?

VulnBump évalue des agents IA sur 307 mises à niveau réelles de dépendances. Une correction n’est validée que si la vulnérabilité disparaît et que la build et les tests restent au vert.