Passer

Security by design : un mindset à adopter

A l’ère de la transformation numérique, le développement, le déploiement et le maintien en production des applications ne cessent d’augmenter et d’accélérer.

Le cap des 19 millions de programmeurs actifs dans le monde a été franchi début 2019, estime la société d’analystes SplashData1 dans un rapport. Au cours de la prochaine décennie, malgré un rythme de croissance moins soutenu, leur population devrait plus que doubler. Les programmeurs seraient 45 millions en 2030.

Les équipes de développement devant accélérer leur fréquence de livraison et les équipes de sécurité faisant face à des menaces de plus en plus sophistiquées et régulières, l’implication de la sécurité dès la conception des applications devient une nécessité.

Nous trouvant donc dans un paradigme de développement agile et de déploiement continu, appliquer la sécurité uniquement à la fin, par un audit de sécurité, n’est plus suffisant, ni pratique. Par ailleurs, corriger une vulnérabilité de sécurité coûte bien plus cher, en temps et argent, quand c’est réalisé en production ou pré-production par rapport aux étapes de conception et de développement.

Qu’importe la technologie : Web, API, Low-Code, No-Code, Embarqué, des logiciels qui proposent des fonctionnalités complexes avec un traitement autonome, Mobile… Les développements doivent aujourd’hui suivre la culture du “Security by design”.

Le Security By Design dans le développement logiciel

Qu’est-ce que l’approche secure by design ?

C’est une démarche stratégique qui vise à limiter le risque de cyberattaque en amont.
On mise ainsi sur l’implémentation de dispositifs de surveillance, de tests et de procédures de sauvegarde cohérents dès la création des logiciels et des équipements. Il s’agit d’intégrer la sécurité aux produits dès leur conception afin de renforcer leur « immunité » à toutes sortes d’attaques.

Les principes d’une conception secure by design

Dans une approche secure by design, plusieurs éléments doivent être pris en compte dans la conception du système. En voici quelques exemples :

  • Comprendre et identifier les risques et menaces auxquels le système va être exposé.
  • Minimiser la surface d’attaque. Pour cela, il est notamment important de restreindre aux utilisateurs l’accès à certaines zones, afin de réduire les points d’entrée pour les violations de sécurité. Les tests d’intrusion peuvent dans ce cadre permettre d’identifier des vulnérabilités dans un système.
  • Distinguer et restreindre les privilèges. Chaque utilisateur doit avoir un rôle défini et des accès restreints au strict nécessaire. L’administrateur peut, au cas par cas, accorder des accès supplémentaires si la demande se justifie. Cette restriction des privilèges permet de réduire le risque de violation de sécurité des utilisateurs.
  • Prendre des précautions et rester vigilant vis-à-vis des services tiers. En cas d’erreur, dans le cadre de fonctions transactionnelles, garder les codes erreurs confidentiels pour ne pas divulguer aux attaquants des informations dont ils pourraient se servir pour mieux identifier le fonctionnement de votre système.

Un intérêt économique

Tel qu’évoqué précédemment, la prise en compte de la sécurité dès la conception permet d’intégrer l’analyse de risques et augmenter la sécurité d’un système. Cette approche par anticipation peut se révéler particulièrement intéressante d’un point de vue économique. En effet en cas d’incident, les dommages seront minimisés si la conception du système est secure by design, autrement dit que les paramètres de risque et de sécurité ont été intégrés directement.

Pour vous donner un exemple concret, les rançongiciels, ou ransomwares, représentent la recherche d’assistance la plus fréquente effectuée par les entreprises sur la plateforme gouvernementale, avec 17 % de demandes. Les entreprises françaises sont de plus en plus souvent victimes de ce type d’attaques visant à leur soutirer une rançon. En seulement un an, celles-ci ont augmenté de 255 % selon l’ANSSI, passant de 54 attaques rapportées en 2019 à 192 attaques en 2020.

Ainsi, définir les actifs sensibles de l’entreprise, sensibiliser ses collaborateurs, se former aux risques de cyberattaques, réaliser des diagnostics par des experts, sont les premières actions à mettre en place pour prévenir les risques.

Les piliers de l’analyse de risques

Quatre notions sont essentielles pour maîtriser son analyse de risques :

  • Confidentialité : faire en sorte que seuls les utilisateurs autorisés puissent avoir accès aux données
  • Intégrité : s’assurer que les données ne soient pas falsifiées ou manipulées par des utilisateurs non autorisés
  • Disponibilité : permettre l’accès aux systèmes et aux données pour les utilisateurs autorisés en fonction de leurs besoins
  • Traçabilité : s’assurer que les journaux d’audit et de monitoring sont bien implémentés et protégés surtout pour les éléments stratégiques

Ces piliers serviront ensuite à la construction de contrôles robustes correspondant aux principes de sécurité ci-après et adaptés aux fonctions applicatives et à leurs criticités.
Ceux-ci permettent également un premier niveau de classification d’une application auquel peut s’ajouter par exemple le critère d’exposition de l’application sur Internet.

Principes de sécurité

Ces principes sont la base du développement sécurisé et permettent de comprendre facilement les exploitations potentielles et surtout de vulgariser des bonnes pratiques dans le but de réduire le risque global.

  1. Minimiser la surface d’attaque

Chaque fonction est par définition un risque de sécurité en plus pour l’application dans son ensemble, donc l’exposition d’une fonction, d’un service ou d’une donnée doit être réalisée pour remplir son besoin initial.
Par exemple :

  • Une fonction de recherche authentifiée peut être un risque d’injection SQL ou de Cross Site Scripting donc elle se doit d’être validée côté serveur afin de réduire ces risques.
  • L’interface d’administration doit-elle être accessible depuis Internet ? Si oui, est-il possible de mettre en place un filtrage IP ou une authentification forte ?
  • Les ports ouverts sur votre machine sont-ils tous utiles au bon fonctionnement applicatif ? Si non pourquoi ne pas les fermer pour éviter tout risque.

2. Établir des valeurs sécurisées par défaut

Chaque fonction doit apporter la meilleure expérience utilisateur possible pour un besoin dans un contexte de sécurité standard. Ce contexte doit être défini soit par l’entreprise de manière globale, soit par le métier. L’équipe de développement pourra en proposer à défaut mais ne devra pas en porter la responsabilité.
Par exemple, la fonction de modification de mot de passe se base sur une politique de caractères, une durée de vie, etc. Dans certains cas, le service commercial peut demander à modifier celle-ci pour ces utilisateurs, néanmoins cela devra être validé et le risque introduit devra être pris en compte dans la prise de décision.

3. Le principe du moindre privilège

Chaque utilisateur doit posséder les droits nécessaires pour réaliser ses processus métier dans l’application et ce de manière minimale. Cela s’applique aussi bien à l’accès aux données qu’aux ressources applicatives et matérielles.
La granularité des droits et l’utilisation de Frameworks peuvent permettre de faciliter la gestion et l’implémentation de ce principe.

4. Le principe de défense en profondeur

Un contrôle peut parfois suffire pour une fonction, néanmoins le point est le durcissement en rajoutant des contrôles selon différents vecteurs potentiels d’attaque. L’objectif est donc de réduire les risques de manière globale et de limiter l’exploitation de vulnérabilités graves ou critiques au maximum.
En développement sécurisé, cela peut se décrire par exemple par la « Ne jamais faire confiance aux entrées utilisateur » « Never trust user input » et ainsi avoir plusieurs niveaux de validations dans chaque fonction.
Un autre exemple, est de faire valider l’autorisation à une fonction d’un utilisateur à chaque appel de cette fonction par l’utilisateur, de tracer les accès et actions en fonction du risque tout en étant capable de bloquer temporairement ou non les appels successifs en cas de problème ou de suspicion d’attaque.

5. Échouer en toute sécurité

Dans les fonctions transactionnelles, la gestion des erreurs est primordiale afin de ne pas fournir de la connaissance à l’attaquant sur la sécurité en place ou les workflows de l’application.
Par exemple, si une personne malveillante essaie d’accéder à une fonction de manière non nominale, elle peut obtenir des codes d’erreurs lui permettant de comprendre la logique de l’application ou pire des stacks d’exceptions lui permettant d’identifier les technologies utilisées et ainsi continuer à peaufiner son attaque future.

6. Ne faites pas confiance aux prestataires externes

L’utilisation de services externes est devenue une pratique courante et requiert une attention particulière.
Les partenaires proposant ces services peuvent avoir des politiques de sécurité et des niveaux de maturité en termes de développement et de sécurité très différents.
La confiance envers un partenaire ne doit jamais être implicite et chaque service doit être traité indépendamment. Un contrat de service est déjà un bon point de départ mais n’assure pas que les données partenaires ne soient pas corrompues un jour. De plus, ce contrat doit permettre d’expliciter ses exigences d’interconnexions et pouvoir permettre des analyses conjointes en cas d’incidents de sécurité.
Par exemple, il est recommandé de prévoir une validation des données avant de les afficher. Demander à avoir un jeton renouvelable sur une durée décente pour utiliser un service distant est aussi une bonne pratique. Cela permettra de s’assurer qu’en cas de vol de ce jeton, la fenêtre d’action de l’attaquant sera la plus limitée possible.

7. Séparation des tâches

Elle a pour but d’éviter les fraudes et les contournements de processus en interne et se base sur la notion de confiance sur la validation de la bonne réalisation d’une tâche dans un contexte bien précis.
Par exemple, un développeur ayant des droits d’administrateur sur la plateforme de développement ne devrait pas les avoir en production et ainsi pouvoir visualiser des données hors de son périmètre d’activités initial.

8. Évitez la sécurité par l’obscurité

Bien qu’étant un contrôle de sécurité, l’obscurité repose sur la non-divulgation d’informations liées à la structure, au fonctionnement et à l’implémentation d’un process informatique.
L’exemple type est l’obfuscation du code qui est censée bloquer toute rétro-ingénierie. Le contre-exemple étant les projets Open Source comme Apache, Linux, GPG entre autres où la sécurité est améliorée par la communauté, malgré le fait que les sources soient publiques.
En cryptologie, tout repose sur le secret de la clé, mais si l’algorithme utilisé est obsolète car l’application est très ancienne par exemple, l’attaquant pourra retrouver la clé initiale via des outils dédiés de brute force et des dictionnaires et ainsi contourner l’obscurité définie pour sécuriser. Des outils d’analyse de cryptographie comme Cryptosense permettent d’auditer les applications et d’évaluer le risque induit et surtout comment remédier.

9. Faites en sorte que la sécurité soit simple et efficace

De la même façon que pour la maintenabilité du code, la sécurité introduite dans l’application doit rester simple tout en assurant sa fonction et permettre son évolutivité dans le temps.
Utiliser directement une librairie standard du marché pour réaliser un protocole OAuth par exemple, plutôt que de redévelopper sa propre implémentation.
Avoir une architecture logicielle simple permet de conserver une bonne isolation dans les différents services et de faciliter la gestion des différents contrôles d’accès.

10. Résoudre les problèmes de sécurité correctement

Si un problème de sécurité est identifié, sa remédiation doit être préalablement testée pour éviter des régressions fonctionnelles.
Elle devra aussi être appliquée de manière globale sur l’ensemble des services et/ou applications impactées pour que la correction soit effective.
Par exemple, si un service Web est corrigé côté serveur sur sa partie autorisation, les clients devront mettre à jour leur implémentation afin de pouvoir clôturer le service vulnérable dans les meilleurs délais.

Conclusion

La sécurité du code est un gage de confiance envers les utilisateurs finaux mais nécessite une compréhension et une appréhension du risque qu’un développeur peut apporter dans une démarche d’amélioration continue.

1 D’après le rapport “The Global Developper Population 2019” :
https://slashdata-website-cms.s3.amazonaws.com/sample_reports/EiWEyM5bfZe1Kug_.pdf

Référence : https://wiki.owasp.org/index.php/Security_by_Design_Principles