Consignes de sécurité

Android dispose de fonctionnalités de sécurité intégrées qui réduisent considérablement la fréquence et l'impact des problèmes de sécurité des applications. Le système est conçu pour que vous puissiez généralement compiler vos applications avec les autorisations système et de fichiers par défaut, et ainsi éviter des décisions difficiles concernant la sécurité.

Les principales fonctionnalités de sécurité suivantes vous aident à créer des applications sécurisées :

  • Le bac à sable de l'application Android, qui isole les données de votre application et l'exécution de code des autres applications.
  • Un framework d'application proposant des implémentations robustes de fonctionnalités de sécurité courantes telles que la cryptographie, les autorisations et la communication inter-processus (IPC) sécurisée.
  • Des technologies telles que la distribution aléatoire de l'espace d'adressage (ASLR), No eXecute (NX), ProPolice,safe_iop, OpenBSD dlmalloc et calloc, et Linux mmap_min_addr pour atténuer les risques associés aux erreurs courantes de gestion de la mémoire.
  • Autorisations accordées aux utilisateurs pour restreindre l'accès aux fonctionnalités système et aux données utilisateur.
  • Autorisations définies par l'application pour contrôler les données de chaque application.

Il est important de connaître les bonnes pratiques de sécurité Android présentées sur cette page. Suivre ces pratiques comme habitudes générales de codage vous permet d'éviter d'introduire par inadvertance des problèmes de sécurité qui nuisent à vos utilisateurs.

Authentification

L'authentification est une condition préalable à de nombreuses opérations de sécurité clés. Pour contrôler l'accès aux éléments protégés tels que les données utilisateur, le fonctionnement de l'application et d'autres ressources, vous devrez ajouter une authentification à votre application Android.

Vous pouvez améliorer l'expérience d'authentification de vos utilisateurs en intégrant votre application au Gestionnaire d'identifiants. Le Gestionnaire d'identifiants est une bibliothèque Android Jetpack qui unifie la compatibilité des API avec la plupart des principales méthodes d'authentification, y compris les clés d'accès, les mots de passe et les solutions de connexion fédérée telles que Se connecter avec Google.

Pour améliorer la sécurité de votre application, envisagez d'ajouter des méthodes d'authentification biométrique telles que les empreintes digitales ou la reconnaissance faciale. Il peut s'agir d'applications de gestion financière, de gestion des soins de santé ou de gestion des identités.

La fonctionnalité Autofill Framework d'Android peut faciliter le processus d'inscription et de connexion, ce qui réduit les taux d'erreur et les frictions pour les utilisateurs. La saisie automatique s'effectue avec les gestionnaires de mots de passe, ce qui permet aux utilisateurs de sélectionner des mots de passe complexes et aléatoires qui peuvent être stockés et récupérés facilement et de manière sécurisée.

Stockage des données

Le problème de sécurité le plus courant d'une application sur Android est de savoir si d'autres applications peuvent accéder aux données que vous enregistrez sur l'appareil. Il existe trois façons essentielles d'enregistrer des données sur l'appareil :

  • Mémoire de stockage interne
  • Stockage externe
  • Fournisseurs de contenu

Les sections suivantes décrivent les problèmes de sécurité associés à chaque approche.

Mémoire de stockage interne

Par défaut, les fichiers que vous créez dans la mémoire de stockage interne ne sont accessibles qu'à votre application. Android implémente cette protection, qui est suffisante pour la plupart des applications.

Évitez les modes MODE_WORLD_WRITEABLE et MODE_WORLD_READABLE obsolètes pour les fichiers d'IPC. Elles ne permettent pas de limiter l'accès aux données à des applications particulières, ni de contrôler le format des données. Si vous souhaitez partager vos données avec d'autres processus d'application, envisagez plutôt d'utiliser un fournisseur de contenu. Celui-ci offre des autorisations de lecture et d'écriture à d'autres applications et peut accorder des autorisations dynamiques au cas par cas.

Stockage externe

Les fichiers créés dans le stockage externe, comme des cartes SD, sont lisibles et accessibles en écriture dans le monde entier. Étant donné que le stockage externe peut être supprimé par l'utilisateur et également modifié par n'importe quelle application, ne stockez que des informations non sensibles dans le stockage externe.

Effectuez une validation des entrées lorsque vous gérez des données d'un stockage externe, comme vous le feriez avec des données provenant de n'importe quelle source non fiable. Ne stockez pas d'exécutables ni de fichiers de classe sur un stockage externe avant le chargement dynamique. Si votre application récupère des fichiers exécutables à partir du stockage externe, assurez-vous qu'ils sont signés et validés de manière cryptographique avant le chargement dynamique.

Fournisseurs de contenu

Les fournisseurs de contenu offrent un mécanisme d'espace de stockage structuré qui peut être limité à votre propre application ou exporté pour permettre à d'autres applications d'y accéder. Si vous ne souhaitez pas permettre à d'autres applications d'accéder à votre ContentProvider, marquez-le comme android:exported=false dans le fichier manifeste de l'application. Sinon, définissez l'attribut android:exported sur true pour permettre aux autres applications d'accéder aux données stockées.

Lorsque vous créez un ContentProvider exporté pour être utilisé par d'autres applications, vous pouvez spécifier une seule autorisation pour la lecture et l'écriture, ou spécifier des autorisations distinctes pour la lecture et l'écriture. Limitez vos autorisations à celles qui sont nécessaires à l'accomplissement de la tâche concernée. N'oubliez pas qu'il est généralement plus facile d'ajouter des autorisations ultérieurement pour exposer de nouvelles fonctionnalités que de les supprimer et d'avoir un impact sur les utilisateurs existants.

Si vous utilisez un fournisseur de contenu pour ne partager des données qu'entre vos propres applications, nous vous recommandons d'utiliser l'attribut android:protectionLevel défini sur la protection signature. Les autorisations de signature ne nécessitent pas de confirmation de l'utilisateur. Elles offrent donc une meilleure expérience utilisateur et un accès plus contrôlé aux données du fournisseur de contenu lorsque les applications qui y accèdent sont signées avec la même clé.

Les fournisseurs de contenu peuvent également fournir un accès plus précis en déclarant l'attribut android:grantUriPermissions et en utilisant les indicateurs FLAG_GRANT_READ_URI_PERMISSION et FLAG_GRANT_WRITE_URI_PERMISSION dans l'objet Intent qui active le composant. Le champ d'application de ces autorisations peut être davantage limité par l'élément <grant-uri-permission>.

Lorsque vous accédez à un fournisseur de contenu, utilisez des méthodes de requête paramétrées telles que query, update et delete() pour éviter toute injection SQL potentielle à partir de sources non fiables. Notez que l'utilisation de méthodes paramétrées n'est pas suffisante si l'argument selection est créé en concaténant les données utilisateur avant de les envoyer à la méthode.

N'ayez pas un faux sentiment de sécurité à propos de l'autorisation d'écriture. L'autorisation d'écriture autorise les instructions SQL qui permettent de confirmer certaines données à l'aide de clauses WHERE de création et d'analyser les résultats. Par exemple, un pirate informatique peut vérifier la présence d'un numéro de téléphone spécifique dans un journal d'appels en ne modifiant une ligne que si ce numéro existe déjà. Si les données du fournisseur de contenu ont une structure prévisible, l'autorisation d'écriture peut être équivalente à celle de lecture et d'écriture.

Autorisations

Étant donné que la plate-forme Android isole les applications les unes des autres dans un système de bac à sable, celles-ci doivent partager leurs ressources et leurs données de manière explicite. Pour ce faire, elles déclarent les autorisations dont elles ont besoin pour des fonctionnalités supplémentaires non fournies par le bac à sable de base, y compris l'accès aux fonctionnalités de l'appareil telles que l'appareil photo.

Demandes d'autorisations

Réduisez le nombre d'autorisations demandées par votre application. Limiter l'accès aux autorisations sensibles réduit le risque d'utilisation abusive par inadvertance, améliore l'adoption par les utilisateurs et rend votre application moins vulnérable aux pirates informatiques. En règle générale, si une autorisation n'est pas nécessaire au fonctionnement de votre application, ne la demandez pas. Consultez le guide pour déterminer si votre application doit demander des autorisations.

Si possible, concevez votre application de sorte qu'elle ne nécessite aucune autorisation. Par exemple, plutôt que de demander l'accès aux informations de l'appareil pour créer un identifiant unique, créez un UUID pour votre application. (Pour en savoir plus, consultez la section concernant les données utilisateur.) Vous pouvez également stocker les données dans la mémoire de stockage interne, plutôt que d'utiliser un stockage externe (qui nécessite une autorisation).

En plus de demander des autorisations, votre application peut utiliser l'élément <permission> pour protéger l'IPC, qui est sensible à la sécurité et qui est exposée à d'autres applications, telles qu'un ContentProvider. En général, nous recommandons dans la mesure du possible d'utiliser des contrôles d'accès autres que les autorisations confirmées par l'utilisateur, car ces autorisations peuvent prêter à confusion. Par exemple, envisagez d'utiliser le niveau de protection de signature sur les autorisations de communication IPC entre les applications fournies par un seul développeur.

Ne divulguez pas de données protégées par des autorisations. Cela se produit lorsque votre application expose des données via l'IPC qui n'est disponible que parce qu'elle est autorisée à y accéder. Les clients de l'interface d'IPC de votre application peuvent ne pas disposer de cette même autorisation d'accès aux données. Pour en savoir plus sur la fréquence et les effets potentiels de ce problème, consultez l'étude Re-délégation de l'autorisation : Attaques et défenses, publiée sur le site d'USENIX.

Définition des autorisations

Définissez le plus petit ensemble d'autorisations répondant à vos exigences de sécurité. La création d'une nouvelle autorisation est relativement rare pour la plupart des applications, car les autorisations définies par le système couvrent de nombreuses situations. Le cas échéant, effectuez des vérifications d'accès à l'aide des autorisations existantes.

Si vous avez besoin d'une nouvelle autorisation, déterminez si vous pouvez accomplir votre tâche avec un niveau de protection de signature. Les autorisations de signature sont transparentes pour l'utilisateur et n'autorisent l'accès que par les applications signées par le même développeur que l'application effectuant la vérification des autorisations.

Si la création d'une nouvelle autorisation est toujours nécessaire, déclarez-la dans le fichier manifeste d'application à l'aide de l'élément <permission>. Les applications utilisant la nouvelle autorisation peuvent la référencer en ajoutant un élément <uses-permission> à leurs fichiers manifestes. Vous pouvez également ajouter des autorisations de manière dynamique à l'aide de la méthode addPermission().

Si vous créez une autorisation avec le niveau de protection dangereuse, vous devez prendre en compte un certain nombre de complexités :

  • L'autorisation doit comporter une chaîne qui exprime de manière concise à l'utilisateur la décision de sécurité qu'il est tenu de prendre.
  • La chaîne d'autorisation doit être localisée dans de nombreuses langues différentes.
  • Les utilisateurs peuvent choisir de ne pas installer une application si une autorisation est source de confusion ou perçue comme risquée.
  • Les applications peuvent demander l'autorisation lorsque son créateur n'a pas été installé.

Chacun de ces éléments représente un défi non technique important pour vous en tant que développeur, mais aussi une source de confusion pour vos utilisateurs. C'est pourquoi nous déconseillons l'utilisation du niveau d'autorisation dangereux.

Mise en réseau

Les transactions réseau présentent un risque intrinsèque pour la sécurité, car elles impliquent la transmission de données potentiellement privées à l'utilisateur. Les utilisateurs sont de plus en plus conscients des problèmes de confidentialité que pose un appareil mobile, en particulier lorsque celui-ci effectue des transactions réseau. Il est donc très important que votre application mette en œuvre toutes les bonnes pratiques pour garantir la sécurité des données de l'utilisateur à tout moment.

Mise en réseau IP

La mise en réseau sur Android n'est pas très différente des autres environnements Linux. Il est essentiel de s'assurer que les protocoles appropriés sont utilisés pour les données sensibles, telles que HttpsURLConnection pour le trafic Web sécurisé. Utilisez HTTPS plutôt que HTTP partout où HTTPS est compatible avec le serveur, car les appareils mobiles se connectent souvent à des réseaux non sécurisés, tels que des points d'accès Wi-Fi publics.

La communication authentifiée et chiffrée au niveau du socket peut être facilement mise en œuvre à l'aide de la classe SSLSocket. Compte tenu de la fréquence à laquelle les appareils Android se connectent aux réseaux sans fil non sécurisés via le Wi-Fi, l'utilisation d'un réseau sécurisé est vivement recommandée pour toutes les applications qui communiquent sur le réseau.

Certaines applications utilisent les ports réseau localhost pour gérer l'IPC sensible. N'utilisez pas cette approche, car ces interfaces sont accessibles par d'autres applications sur l'appareil. Utilisez plutôt un mécanisme d'IPC Android où l'authentification est possible, par exemple avec un Service. La liaison à l'adresse IP non spécifique INADDR_ANY est pire que l'utilisation du rebouclage, car elle permet à votre application de recevoir des requêtes provenant de n'importe quelle adresse IP.

Assurez-vous de ne pas faire confiance aux données téléchargées à partir du protocole HTTP ou d'autres protocoles non sécurisés. Cela inclut la validation des entrées dans WebView et toutes les réponses aux intents émis vers HTTP.

Réseau de la téléphonie

Le protocole de SMS a été principalement conçu pour la communication entre utilisateurs et n'est pas adapté aux applications qui souhaitent transférer des données. En raison des limites des SMS, nous vous recommandons d'utiliser Firebase Cloud Messaging (FCM) et la mise en réseau IP pour envoyer des messages de données à partir d'un serveur Web vers votre application sur l'appareil d'un utilisateur.

Sachez que les SMS ne sont ni chiffrés, ni fortement authentifiés, que ce soit sur le réseau ou sur l'appareil. En particulier, tout destinataire d'un SMS doit s'attendre à ce qu'un utilisateur malveillant ait envoyé le SMS à votre application. Ne vous fiez pas à des données SMS non authentifiées pour exécuter des commandes sensibles. Sachez également que les SMS peuvent faire l'objet d'une attaque de spoofing et/ou d'interception sur le réseau. Sur l'appareil Android lui-même, les SMS sont transmis en tant qu'intents de diffusion. Ils peuvent donc être lus ou capturés par d'autres applications disposant de l'autorisation READ_SMS.

Validation des entrées

La validation des entrées insuffisante est l'un des problèmes de sécurité les plus courants qui affectent les applications, quelle que soit la plate-forme sur laquelle elles s'exécutent. Android dispose de contre-mesures au niveau de la plate-forme qui réduisent l'exposition des applications aux problèmes de validation des entrées. Nous vous recommandons d'utiliser ces fonctionnalités lorsque cela est possible. En outre, nous vous recommandons d'utiliser des langages avec sûreté du typage afin de réduire le risque de problèmes de validation des entrées.

Si vous utilisez du code natif, toute donnée lue à partir de fichiers, reçue sur le réseau ou reçue d'une IPC peut potentiellement présenter un problème de sécurité. Les problèmes les plus courants sont les dépassements de mémoire tampon, les erreurs d'utilisation après libération et les erreurs de type off-by-one. Android fournit un certain nombre de technologies, telles qu'ASLR et DEP (prévention de l'exécution des données), qui réduisent l'exploitabilité de ces erreurs, mais ne résolvent pas le problème sous-jacent. Vous pouvez éviter ces failles en gérant soigneusement les pointeurs et les tampons.

Les langages dynamiques basés sur des chaînes tels que JavaScript et SQL sont également soumis à des problèmes de validation des entrées dus aux caractères d'échappement et à l'injection de script.

Si vous utilisez des données dans des requêtes envoyées à une base de données SQL ou à un fournisseur de contenu, alors l'injection SQL peut poser problème. La meilleure défense consiste à utiliser des requêtes paramétrées, comme indiqué dans la section sur les fournisseurs de contenu. Limiter les autorisations en lecture seule ou en écriture seule peut également réduire les risques liés à l'injection SQL.

Si vous ne pouvez pas utiliser les fonctionnalités de sécurité abordées dans cette section, assurez-vous d'utiliser des formats de données bien structurées et de vérifier que les données sont conformes au format attendu. Bien que le blocage de caractères spécifiques ou le remplacement de caractères puissent être une stratégie efficace, ces techniques sont souvent sources d'erreurs. Nous vous recommandons de les éviter dans la mesure du possible.

Données utilisateur

La meilleure approche pour la sécurité des données utilisateur consiste à minimiser l'utilisation d'API qui accèdent à des informations sensibles ou personnelles. Si vous avez accès aux données utilisateur, évitez autant que possible de les stocker ou de les transmettre. Déterminez si votre logique d'application peut être mise en œuvre en utilisant les données sous une forme non-réversible ou hachée. Par exemple, votre application peut utiliser la forme hachée d'une adresse e-mail en tant que clé primaire pour éviter de transmettre ou de stocker l'adresse e-mail. Les risques de divulguer des données par inadvertance sont ainsi réduits, de même que les risques de piratage informatique en vue d'exploiter votre application.

Authentifiez votre utilisateur chaque fois qu'un accès à des données privées est requis, et utilisez des méthodes d'authentification modernes telles que les clés d'accès et le Gestionnaire d'identifiants. Si votre application a besoin d'accéder à des informations personnelles, gardez à l'esprit que certaines juridictions peuvent vous demander de fournir des règles de confidentialité expliquant comment vous utilisez et stockez ces données. Suivez les bonnes pratiques en matière de sécurité qui consistent à réduire l'accès aux données utilisateur pour simplifier la conformité.

Déterminez également si votre application pourrait exposer par inadvertance des informations personnelles à d'autres parties, telles que des composants tiers pour la publicité ou des services tiers utilisés par votre application. Si vous ne savez pas pourquoi un composant ou un service nécessite des informations personnelles, ne les fournissez pas. En général, la réduction de l'accès de votre application aux informations personnelles réduit le risque de problèmes dans ce domaine.

Si votre application nécessite l'accès à des données sensibles, déterminez si vous devez les transmettre à un serveur ou si vous pouvez exécuter l'opération directement sur le client. Envisagez d'exécuter tout code utilisant des données sensibles sur le client afin d'éviter de transmettre les données de l'utilisateur. Assurez-vous également de ne pas exposer involontairement des données utilisateur à d'autres applications de l'appareil via une IPC trop permissive, des fichiers accessibles en écriture ou des sockets réseau. L'IPC trop permissive est un cas particulier de fuite de données protégées par des autorisations, abordé dans la section Demandes d'autorisation.

Si un GUID (identifiant unique global) est requis, créez un numéro unique volumineux et stockez-le. N'utilisez pas d'identifiants de téléphone, tels que le numéro de téléphone ou le code IMEI, qui peuvent être associés à des informations personnelles. Ce thème est davantage abordé sur la page des bonnes pratiques concernant les identifiants uniques.

Soyez prudent lorsque vous écrivez dans des journaux sur l'appareil. Sur Android, les journaux sont une ressource partagée et sont disponibles pour une application disposant de l'autorisation READ_LOGS. Même si les données du journal du téléphone sont temporaires et effacées au redémarrage, une journalisation inappropriée des informations sur l'utilisateur peut, par inadvertance, entraîner une fuite des données utilisateur à d'autres applications. En plus de ne pas consigner les informations permettant d'identifier personnellement l'utilisateur, limitez l'utilisation des journaux dans les applications de production. Pour faciliter l'implémentation, utilisez des indicateurs de débogage et des classes Log personnalisées avec des niveaux de journalisation facilement configurables.

WebView

Étant donné que WebView consomme du contenu Web pouvant inclure du code HTML et JavaScript, une utilisation incorrecte peut entraîner des problèmes de sécurité Web courants, tels que les scripts intersites (injection JavaScript). Android inclut un certain nombre de mécanismes permettant de réduire le champ d'application de ces problèmes potentiels en limitant la capacité de WebView aux fonctionnalités minimales requises par votre application.

Si votre application n'utilise pas directement JavaScript dans un élément WebView, n'appelez pas setJavaScriptEnabled. Certains exemples de code utilisent cette méthode. Si vous réutilisez un exemple de code qui l'utilise dans une application de production, supprimez cet appel de méthode s'il n'est pas nécessaire. Par défaut, WebView n'exécute pas JavaScript. Les scripts intersites ne sont donc pas possibles.

Utilisez addJavaScriptInterface() avec une attention particulière, car il permet à JavaScript d'appeler des opérations normalement réservées aux applications Android. Si vous l'utilisez, exposez addJavaScriptInterface() uniquement aux pages Web à partir desquelles toutes les entrées sont fiables. Si une entrée non fiable est autorisée, un code JavaScript non approuvé pourrait appeler des méthodes Android dans votre application. En général, nous vous recommandons de n'exposer addJavaScriptInterface() qu'au code JavaScript contenu dans l'APK de votre application.

Si votre application accède à des données sensibles avec un WebView, envisagez d'utiliser la méthode clearCache() pour supprimer tous les fichiers stockés localement. Vous pouvez également utiliser des en-têtes côté serveur, tels que no-store, pour indiquer qu'une application ne doit pas mettre en cache un contenu particulier.

Les appareils exécutant des plates-formes antérieures à Android 4.4 (niveau d'API 19) utilisent une version de webkit qui présente un certain nombre de problèmes de sécurité. Pour contourner ce problème, si votre application s'exécute sur ces appareils, elle doit vérifier que les objets WebView n'affichent que du contenu fiable. Pour vous assurer que votre application n'est pas exposée à des failles potentielles dans SSL, utilisez l'objet de sécurité Provider pouvant être mis à jour comme décrit dans la section Mettre à jour le fournisseur de solutions de sécurité pour vous protéger contre les failles SSL. Si votre application doit afficher du contenu à partir du Web ouvert, envisagez de fournir votre propre moteur de rendu afin de le maintenir à jour avec les derniers correctifs de sécurité.

Demandes d'identifiants

Les demandes d'identifiants sont un vecteur d'attaque. Voici quelques conseils pour vous aider à renforcer la sécurité des demandes d'identifiants dans vos applications Android.

Réduire l'exposition des identifiants

  • Évitez les demandes d'identifiants inutiles. Pour que les attaques par hameçonnage soient plus visibles et moins susceptibles de réussir, réduisez la fréquence des demandes d'identifiants des utilisateurs. Utilisez plutôt un jeton d'autorisation et actualisez-le. Ne demandez que la quantité minimale d'informations d'identification nécessaire pour l'authentification et l'autorisation.
  • Stockez les identifiants de manière sécurisée. Utilisez le Gestionnaire d'identifiants pour activer l'authentification sans mot de passe à l'aide de clés d'accès ou pour implémenter une connexion fédérée à l'aide de schemes tels que Se connecter avec Google. Si vous devez utiliser l'authentification par mot de passe traditionnelle, ne stockez pas d'ID utilisateur ni de mots de passe sur l'appareil. Effectuez plutôt l'authentification initiale à l'aide du nom d'utilisateur et du mot de passe fournis par l'utilisateur, puis utilisez un jeton d'autorisation spécifique au service et de courte durée.
  • Limitez le champ d'application des autorisations. Ne demandez pas d'autorisations étendues pour une tâche qui ne nécessite qu'un champ d'application plus restreint.
  • Limitez les jetons d'accès. Utilisez des opérations de jetons et des appels d'API de courte durée.
  • Limitez les taux d'authentification. Les requêtes d'authentification ou d'autorisation rapides et successives peuvent être le signe d'une attaque par force brute. Limitez ces taux à une fréquence raisonnable tout en permettant une expérience fonctionnelle et conviviale avec l'application.

Utiliser l'authentification sécurisée

  • Implémentez des clés d'accès. Activez les clés d'accès pour mettre à niveau les mots de passe de manière plus sécurisée et plus conviviale.
  • Ajoutez l'authentification biométrique. Offrez la possibilité d'utiliser l'authentification biométrique comme la reconnaissance faciale ou l'empreinte digitale, pour plus de sécurité.
  • Utilisez des fournisseurs d'identité fédérée. Le Gestionnaire d'identifiants est compatible avec les fournisseurs d'authentification fédérée tels que Se connecter avec Google.
  • Chiffrez la communication. Utilisez HTTPS et des technologies similaires pour vous assurer que les données envoyées par votre application sur un réseau sont protégées.

S'entraîner à gérer des comptes sécurisés

  • Connectez-vous aux services accessibles à plusieurs applications à l'aide d'AccountManager. Si possible, utilisez la classe AccountManager pour appeler un service dans le cloud et ne stockez pas de mots de passe sur l'appareil.
  • Après avoir utilisé AccountManager pour récupérer un Account, utilisez CREATOR avant de transmettre des identifiants afin de ne pas transmettre par inadvertance des identifiants à la mauvaise application.
  • Si les identifiants ne sont utilisés que par les applications que vous créez, vous pouvez vérifier l'application qui accède à AccountManager à l'aide de checkSignatures. Si une seule application utilise les identifiants, vous pouvez également utiliser un KeyStore pour le stockage.

Rester vigilant

  • Assurez-vous que votre code est à jour. Veillez à mettre à jour votre code source, y compris les bibliothèques et dépendances tierces, pour vous prémunir des dernières failles.
  • Surveillez les activités suspectes. Recherchez les éventuels usages abusifs, tels que les schémas d'usage abusif des autorisations.
  • Effectuez un audit de votre code. Effectuez des contrôles de sécurité réguliers sur votre codebase pour détecter d'éventuels problèmes de demande d'identifiants.

Gestion des clés API

Les clés API sont un composant essentiel de nombreuses applications Android. Elles leur permettent d'accéder à des services externes et d'effectuer des fonctions essentielles, telles que la connexion à des services de cartographie, d'authentification et de météo. Toutefois, la divulgation de ces clés sensibles peut avoir de graves conséquences, y compris des violations de données, des accès non autorisés et des pertes financières. Pour éviter de tels scénarios, les développeurs doivent mettre en œuvre des stratégies sécurisées pour gérer les clés API tout au long du processus de développement.

Pour protéger les services contre un usage abusif, les clés API doivent être soigneusement protégées. Pour sécuriser une connexion entre l'application et un service utilisant une clé API, vous devez sécuriser l'accès à l'API. Une fois que votre application est compilée et que son code source inclut des clés API, un pirate informatique peut décompiler l'application et trouver ces ressources.

Cette section est destinée à deux groupes de développeurs Android : ceux qui travaillent avec des équipes d'infrastructure sur leur pipeline de livraison continue et ceux qui déploient des applications autonomes sur le Play Store. Cette section définit les bonnes pratiques à suivre pour gérer les clés API afin que votre application puisse communiquer avec les services de manière sécurisée.

Génération et stockage

Les développeurs doivent considérer le stockage de clés API comme un composant essentiel de la protection des données et de la confidentialité des utilisateurs en adoptant une approche de défense en profondeur.

Stockage de clés sécurisées

Pour une sécurité optimale de la gestion des clés, utilisez Android Keystore et chiffrez les clés stockées à l'aide d'un outil robuste tel que la bibliothèque de chiffrement de sécurité Jetpack ou Tink Java.

L'exemple suivant utilise la bibliothèque de chiffrement de sécurité Jetpack pour créer des préférences partagées chiffrées.

Kotlin

val masterKey = MasterKey.Builder(context)
  .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
  .build()

val encryptedSharedPreferences = EncryptedSharedPreferences.create(
  context,
  "secret_shared_prefs",
  masterKey,
  EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
  EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

// use the shared preferences and editor as you normally would
encryptedSharedPreferences.edit()

Java

MasterKey masterKey = new MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build();

SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
    context,
    "secret_shared_prefs",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

// use the shared preferences and editor as you normally would
SharedPreferences.Editor editor = sharedPreferences.edit();

Exclusion du contrôle du code source

N'effectuez jamais de commit de clés API vers votre dépôt de code source. L'ajout de clés API au code source risque d'exposer les clés à des dépôts publics, à des exemples de code partagés et à des fichiers partagés accidentellement. Utilisez plutôt des plug-ins Gradle, tels que le plug-in Secrets Gradle, pour utiliser les clés API dans votre projet.

Clés spécifiques à l'environnement

Si possible, utilisez des environnements de développement, de test et de production distincts pour les clés API. Utilisez des clés spécifiques à chaque environnement pour isoler chacun d'entre eux, ce qui réduit le risque d'exposer des données de production et vous permet de désactiver les clés compromises sans affecter votre environnement de production.

Contrôle de l'utilisation et des accès

Des pratiques sécurisées pour vos clés API sont essentielles pour protéger votre API et vos utilisateurs. Voici comment préparer vos clés pour une sécurité optimale :

  • Générez des clés uniques pour chaque application. Utilisez des clés API distinctes pour chaque application afin d'identifier et d'isoler les accès compromis.
  • Mettez en place des restrictions d'adresses IP. Si possible, limitez l'utilisation des clés API à des adresses ou plages d'adresses IP spécifiques.
  • Limitez l'utilisation des clés d'applications mobiles. Limitez l'utilisation des clés API à des applications mobiles spécifiques en les regroupant avec la clé ou en utilisant des certificats d'application.
  • Consignez et surveillez les activités suspectes. Mettez en œuvre des mécanismes de journalisation et de surveillance de l'utilisation des API pour détecter les activités suspectes et éviter tout abus potentiel.

Remarque : Votre service doit fournir des fonctionnalités permettant de restreindre les clés à un package ou à une plate-forme particulière. Par exemple, l'API Google Maps limite l'accès par clé en fonction du nom du package et de la clé de signature.

OAuth 2.0 fournit un framework pour autoriser l'accès aux ressources. Il définit des normes d'interaction entre les clients et les serveurs, et permet une autorisation sécurisée. Vous pouvez utiliser OAuth 2.0 pour limiter l'utilisation de la clé API à des clients spécifiques et définir le champ d'application de l'accès afin que chaque clé API ne dispose que du niveau d'accès minimal requis pour son utilisation prévue.

Rotation et expiration des clés

Pour réduire le risque d'accès non autorisé lié à des failles d'API non découvertes, il est important d'alterner régulièrement les clés API. La norme ISO 27001 définit un framework de conformité pour la fréquence d'exécution de la rotation des clés. Dans la plupart des cas, une période de rotation des clés comprise entre 90 jours et 6 mois devrait être suffisante. La mise en œuvre d'un système robuste de gestion des clés peut vous aider à simplifier ces processus, ce qui améliore l'efficacité de vos besoins de rotation et d'expiration des clés.

Bonnes pratiques générales

  • Utilisez SSL/HTTPS. Utilisez toujours la communication HTTPS pour chiffrer vos requêtes API.
  • Épinglez les certificats. Pour ajouter un niveau de sécurité supplémentaire, vous pouvez envisager la mise en œuvre de l'épinglage de certificats afin de vérifier quels certificats sont considérés comme valides.
  • Validez et nettoyez les entrées utilisateur. Validez et nettoyez les entrées utilisateur pour éviter les attaques par injection qui pourraient exposer les clés API.
  • Suivez les bonnes pratiques de sécurité. Appliquez les bonnes pratiques de sécurité générales dans votre processus de développement, y compris les techniques de codage sécurisé, les revues de code et l'analyse des failles.
  • Restez informé. Tenez-vous au courant des dernières menaces de sécurité et des bonnes pratiques de gestion des clés API.
  • Tenez vos SDK à jour. Assurez-vous que vos SDK et bibliothèques sont mis à jour vers la dernière version.

Cryptographie

En plus d'assurer l'isolation des données, la compatibilité du chiffrement complet du système de fichiers et la mise à disposition de canaux de communication sécurisés, Android fournit un large éventail d'algorithmes pour protéger les données à l'aide de la cryptographie.

Sachez quels fournisseurs de sécurité Java Cryptography Architecture (JCA) votre logiciel utilise. Essayez d'utiliser le niveau le plus élevé de l'implémentation de framework préexistante qui puisse prendre en charge votre cas d'utilisation. Le cas échéant, utilisez les fournisseurs proposés par Google dans l'ordre spécifié par Google.

Si vous devez récupérer un fichier de manière sécurisée à partir d'un emplacement réseau connu, un URI HTTPS simple peut suffire et ne nécessite aucune connaissance en cryptographie. Si vous avez besoin d'un tunnel sécurisé, envisagez d'utiliser HttpsURLConnection ou SSLSocket plutôt que d'écrire votre propre protocole. Si vous utilisez SSLSocket, sachez qu'il n'effectue pas de validation de nom d'hôte. Consultez la section Avertissements concernant l'utilisation directe de SSLSocket.

Si vous avez besoin d'implémenter votre propre protocole, n'implémentez pas vos propres algorithmes cryptographiques. Utilisez des algorithmes cryptographiques existants, tels que les implémentations d'AES et de RSA fournies dans la classe Cipher. Suivez également les bonnes pratiques suivantes :

  • Utilisez l'algorithme AES 256 bits à des fins commerciales (s'il n'est pas disponible, utilisez l'algorithme AES 128 bits).
  • Utilisez des tailles de clés publiques de 224 ou 256 bits pour la cryptographie à courbe elliptique (EC).
  • Sachez quand utiliser les modes de blocage CBC, CTR ou GCM.
  • Évitez de réutiliser des IV/compteurs en mode CTR. Assurez-vous qu'ils sont cryptographiquement aléatoires.
  • Lorsque vous utilisez le chiffrement, mettez en œuvre l'intégrité à l'aide du mode CBC ou CTR avec l'une des fonctions suivantes :
    • HMAC-SHA1
    • HMAC-SHA-256
    • HMAC-SHA-512
    • Mode GCM

Utilisez un générateur de nombres aléatoires sécurisé, SecureRandom, pour initialiser les clés cryptographiques générées par KeyGenerator. L'utilisation d'une clé qui n'est pas générée avec un générateur de nombres aléatoires sécurisé affaiblit considérablement la puissance de l'algorithme et peut permettre des attaques hors connexion.

Si vous devez stocker une clé pour une utilisation répétée, utilisez un mécanisme, tel que KeyStore, qui fournit un stockage et une récupération à long terme des clés cryptographiques.

Communication inter-processus

Certaines applications tentent d'implémenter l'IPC à l'aide de techniques Linux traditionnelles, telles que les sockets réseau et les fichiers partagés. Toutefois, nous vous recommandons d'utiliser plutôt les fonctionnalités du système Android pour l'IPC, telles qu'Intent, Binder ou Messenger avec Service et BroadcastReceiver. Les mécanismes d'IPC Android vous permettent de vérifier l'identité de l'application qui se connecte à l'IPC et de définir une stratégie de sécurité pour chacun d'eux.

De nombreux composants de sécurité sont partagés entre les mécanismes d'IPC. Si votre mécanisme d'IPC n'est pas destiné à être utilisé par d'autres applications, définissez l'attribut android:exported sur false dans l'élément du fichier manifeste du composant, comme pour l'élément <service>. Ceci est utile pour les applications qui comprennent plusieurs processus au sein du même UID ou si vous décidez, à un stade ultérieur du développement, que vous ne souhaitez pas réellement exposer une fonctionnalité en tant qu'IPC, mais que vous ne voulez pas réécrire le code.

Si d'autres applications peuvent accéder à votre IPC, vous pouvez appliquer une stratégie de sécurité à l'aide de l'élément <permission>. Si l'IPC se situe entre des applications qui vous appartiennent et qui sont signées avec la même clé, utilisez une autorisation de signature-level dans android:protectionLevel.

Intents

Pour les activités et les broadcast receivers, les intents sont le mécanisme privilégié de l'IPC asynchrone sur Android. En fonction des exigences de votre application, vous pouvez utiliser sendBroadcast, sendOrderedBroadcast ou un intent explicite pour un composant d'application spécifique. Pour des raisons de sécurité, il est préférable d'utiliser des intents explicites.

Attention : Si vous utilisez un intent pour créer une liaison à un **Service**, utilisez un intent explicite pour sécuriser votre application. L'utilisation d'un intent implicite pour démarrer un service représente un risque de sécurité, car vous ne pouvez pas savoir avec certitude quel service répondra à l'intent et l'utilisateur ne peut pas voir quel service démarre. À partir d'Android 5.0 (niveau d'API 21), le système génère une exception si vous appelez **bindService()** avec un intent implicite.

Notez que les annonces ordonnées peuvent être utilisées par un destinataire. Il est donc possible qu'elles ne soient pas diffusées à toutes les applications. Si vous envoyez un intent qui doit être diffusé à un destinataire spécifique, vous devez utiliser un intent explicite qui déclare le récepteur par son nom.

Les expéditeurs d'un intent peuvent vérifier que le destinataire dispose de l'autorisation en spécifiant une autorisation non nulle avec l'appel de méthode. Seules les applications disposant de cette autorisation reçoivent l'intent. Si les données d'un intent de diffusion sont sensibles, envisagez d'appliquer une autorisation pour vous assurer que les applications malveillantes ne peuvent pas s'inscrire pour recevoir ces messages sans les autorisations appropriées. Dans ce cas, vous pouvez également envisager d'appeler directement le récepteur, plutôt que de déclencher une annonce.

Remarque : Les filtres d'intent ne sont pas des fonctionnalités de sécurité. Les composants peuvent être appelés avec des intents explicites et peuvent ne pas contenir de données conformes au filtre d'intent. Pour vérifier qu'il est correctement formaté pour le récepteur, le service ou l'activité appelée, effectuez une validation d'entrée dans votre récepteur d'intent.

Services

Un Service est souvent utilisé pour fournir des fonctionnalités à d'autres applications à utiliser. Chaque classe de service doit avoir une déclaration <service> correspondante dans son fichier manifeste.

Par défaut, les services ne sont pas exportés et ne peuvent être appelés par aucune autre application. Toutefois, si vous ajoutez des filtres d'intent à la déclaration de service, ils sont exportés par défaut. Il est préférable de déclarer explicitement l'attribut android:exported pour vous assurer qu'il se comporte comme vous le souhaitez. Les services peuvent également être protégés à l'aide de l'attribut android:permission. Ainsi, les autres applications doivent déclarer un élément <uses-permission> correspondant dans leur propre fichier manifeste pour pouvoir démarrer, s'arrêter ou s'associer au service.

Remarque : Si votre application cible Android 5.0 (niveau d'API 21) ou une version ultérieure, utilisez **JobScheduler** pour exécuter les services en arrière-plan.

Un service peut protéger les appels d'IPC individuels qui y sont effectués avec des autorisations. Pour ce faire, appelez checkCallingPermission() avant d'exécuter la mise en œuvre de l'appel. Nous vous recommandons d'utiliser les autorisations déclaratives dans le fichier manifeste, car elles sont moins sujettes à la surveillance.

Attention : Ne confondez pas les autorisations client et serveur. Assurez-vous que l'application appelée dispose des autorisations appropriées et que vous accordez les mêmes autorisations à l'application appelante.

Interfaces de liaison et Messenger

L'utilisation de Binder ou de Messenger est le mécanisme privilégié pour l'IPC de style RPC sur Android. Ils fournissent des interfaces bien définies qui permettent une authentification mutuelle des points de terminaison, si nécessaire.

Nous vous recommandons de concevoir les interfaces de votre application de sorte qu'elles ne nécessitent pas de vérification d'autorisations spécifiques à l'interface. Les objets Binder et Messenger ne sont pas déclarés dans le fichier manifeste de l'application. Vous ne pouvez donc pas leur appliquer directement des autorisations déclaratives. Ils héritent généralement des autorisations déclarées dans le fichier manifeste de l'application pour l'élément Service ou Activity dans lequel ils sont implémentés. Si vous créez une interface qui nécessite une authentification et/ou des contrôles d'accès, vous devez ajouter ces contrôles explicitement sous forme de code dans l'interface Binder ou Messenger.

Si vous fournissez une interface nécessitant des contrôles d'accès, utilisez checkCallingPermission() pour vérifier si l'appelant dispose d'une autorisation requise. Cela est particulièrement important avant d'accéder à un service de la part de l'appelant, car l'identité de votre application est transmise à d'autres interfaces. Si vous appelez une interface fournie par un Service, l'appel bindService() peut échouer si vous n'êtes pas autorisé à accéder au service donné. Si vous devez autoriser un processus externe à interagir avec votre application, mais qu'il ne dispose pas des autorisations nécessaires pour le faire, vous pouvez utiliser la méthode clearCallingIdentity(). Cette méthode effectue l'appel vers l'interface de votre application comme si votre application effectuait l'appel elle-même, et non vers l'appelant externe. Vous pouvez restaurer les autorisations de l'appelant ultérieurement à l'aide de la méthode restoreCallingIdentity().

Pour en savoir plus sur l'exécution de l'IPC avec un service, consultez la page Services liés.

Broadcast receivers

Un BroadcastReceiver gère les requêtes asynchrones initiées par un Intent.

Par défaut, les récepteurs sont exportés et peuvent être appelés par n'importe quelle autre application. Si votre BroadcastReceiver est destiné à être utilisé par d'autres applications, vous souhaiterez sans doute appliquer des autorisations de sécurité aux récepteurs à l'aide de l'élément <receiver> dans le fichier manifeste de l'application. Cela empêche les applications ne disposant pas des autorisations appropriées d'envoyer un intent à BroadcastReceiver.

Sécurité avec du code chargé de manière dynamique

Nous vous déconseillons vivement de charger du code en dehors de l'APK de votre application. Une telle pratique augmente considérablement la probabilité que votre application soit compromise par une injection ou une falsification de code. Elle complique également la gestion des versions et les tests des applications, et peut rendre la vérification du comportement d'une application impossible, raison pour laquelle ce type de pratique peut être interdit dans certains environnements.

Si votre application charge du code de manière dynamique, gardez surtout à l'esprit que le code chargé de manière dynamique s'exécute avec les mêmes autorisations de sécurité que l'APK de l'application. L'utilisateur décide d'installer votre application sur la base de votre identité et s'attend à ce que vous fournissiez tout code exécuté dans l'application, y compris le code chargé de manière dynamique.

De nombreuses applications tentent de charger du code à partir d'emplacements non sécurisés, tels que ceux téléchargés à partir du réseau via des protocoles non chiffrés ou à partir d'emplacements accessibles en écriture tels qu'un stockage externe. Ces emplacements peuvent permettre à un utilisateur du réseau de modifier le contenu en transit ou à une autre application sur l'appareil d'un utilisateur de modifier le contenu sur l'appareil. En revanche, les modules inclus directement dans votre APK ne peuvent pas être modifiés par d'autres applications. Cela est vrai que le code soit une bibliothèque native ou une classe chargée à l'aide de DexClassLoader.

Sécurité dans une machine virtuelle

Dalvik est la machine virtuelle (VM) d'exécution d'Android. Dalvik a été conçu spécifiquement pour Android, mais de nombreux problèmes liés au code sécurisé dans d'autres machines virtuelles s'appliquent également à Android. En général, vous n'avez pas à vous soucier des problèmes de sécurité liés à la machine virtuelle. Votre application s'exécute dans un environnement de bac à sable sécurisé, de sorte que les autres processus du système ne peuvent pas accéder à votre code ni à vos données privées.

Si vous souhaitez en savoir plus sur la sécurité des machines virtuelles, familiarisez-vous avec certains documents existants sur ce sujet. Voici deux des ressources les plus populaires :

Ce document porte sur des domaines spécifiques à Android ou différents des autres environnements de VM. Pour les développeurs expérimentés en programmation de VM dans d'autres environnements, deux problèmes généraux peuvent être différents concernant l'écriture d'applications pour Android :

  • Certaines machines virtuelles, telles que la JVM ou l'exécution .NET, agissent comme une limite de sécurité en isolant le code des capacités du système d'exploitation sous-jacent. Sur Android, la VM Dalvik n'est pas une limite de sécurité. Le bac à sable de l'application est mis en œuvre au niveau du système d'exploitation, de sorte que Dalvik peut interagir avec le code natif de la même application sans aucune contrainte de sécurité.
  • Compte tenu du stockage limité sur les appareils mobiles, il est courant que les développeurs souhaitent créer des applications modulaires et utiliser le chargement de classe dynamique. Lors de cette opération, tenez compte de la source dans laquelle vous récupérez la logique de votre application et de son emplacement de stockage local. N'utilisez pas le chargement de classe dynamique à partir de sources non validées, telles que des sources réseau non sécurisées ou un stockage externe, car ce code pourrait être modifié pour inclure un comportement malveillant.

Sécurité en code natif

En général, nous vous recommandons d'utiliser le SDK Android pour le développement d'applications plutôt que du code natif avec le NDK Android. Les applications conçues avec du code natif sont plus complexes, moins portables et incluent davantage d'erreurs courantes de corruption de mémoire telles que les dépassements de mémoire tampon.

Android est conçu à l'aide du noyau Linux. Il est donc particulièrement utile de connaître les bonnes pratiques en matière de sécurité pour le développement Linux si vous utilisez du code natif. Les pratiques en matière de sécurité Linux n'entrent pas dans le champ d'application de ce document, mais le site Programmation sécurisée HOWTO : Création de logiciels sécurisés est l'une des ressources les plus populaires.

Le bac à sable d'application constitue une différence importante entre Android et la plupart des environnements Linux. Sur Android, toutes les applications s'exécutent dans le bac à sable, y compris celles écrites en code natif. Pour les développeurs familiarisés avec Linux, une bonne façon d'y penser est de savoir que chaque application se voit attribuer un identifiant utilisateur unique (UID) avec des autorisations très limitées. Ce point est abordé plus en détail sur la page Présentation de la sécurité Android. Vous devez connaître les autorisations d'application, même si vous utilisez du code natif.