RĂ©cemment, ils parlent de plus en plus de l'analyse statique comme l'un des moyens importants d'assurer la qualitĂ© des produits logiciels en cours de dĂ©veloppement, en particulier du point de vue de la sĂ©curitĂ©. L'analyse statique vous permet de trouver des vulnĂ©rabilitĂ©s et autres erreurs, elle peut ĂȘtre utilisĂ©e dans le processus de dĂ©veloppement, s'intĂ©grant dans des processus personnalisĂ©s. Cependant, en relation avec son application, de nombreuses questions se posent. Quelle est la diffĂ©rence entre les outils payants et gratuits? Pourquoi ne suffit-il pas d'utiliser le linter? En fin de compte, qu'est-ce que la statistique a Ă voir avec cela? Essayons de le comprendre.

Répondons tout de suite à la derniÚre question - les statistiques n'y sont pour rien, bien que l'analyse statique soit souvent appelée à tort statistique. L'analyse est statique, car l'application ne démarre pas pendant la numérisation.
Voyons d'abord ce que nous voulons rechercher dans le code du programme. L'analyse statique est le plus souvent utilisĂ©e pour rechercher des vulnĂ©rabilitĂ©s - des sections de code, dont la prĂ©sence peut entraĂźner une violation de la confidentialitĂ©, de l'intĂ©gritĂ© ou de la disponibilitĂ© du systĂšme d'information. Cependant, les mĂȘmes technologies peuvent ĂȘtre utilisĂ©es pour rechercher d'autres erreurs ou fonctionnalitĂ©s de code.
Nous réservons qu'en général le problÚme de l'analyse statique est algorithmiquement insoluble (par exemple, par le théorÚme de Rice). Par conséquent, vous devez soit limiter les conditions de la tùche, soit autoriser l'inexactitude des résultats (ignorer les vulnérabilités, donner des faux positifs). Il s'avÚre que sur les vrais programmes, la deuxiÚme option s'avÚre fonctionner.
Il existe de nombreux outils payants et gratuits qui revendiquent la recherche de vulnĂ©rabilitĂ© dans des applications Ă©crites dans diffĂ©rents langages de programmation. Voyons comment lâanalyseur statique est gĂ©nĂ©ralement organisĂ©. De plus, nous nous concentrerons sur le noyau et les algorithmes de l'analyseur. Bien sĂ»r, les outils peuvent diffĂ©rer par la convivialitĂ© de l'interface, par l'ensemble de fonctionnalitĂ©s, par l'ensemble de plug-ins pour diffĂ©rents systĂšmes et par la facilitĂ© d'utilisation de l'API. C'est probablement un sujet pour un article sĂ©parĂ©.
Présentation intermédiaire
On peut distinguer trois étapes de base dans le schéma de fonctionnement d'un analyseur statique.
- Construction d'une représentation intermédiaire (une représentation intermédiaire est également appelée représentation interne ou modÚle de code).
- L'utilisation d'algorithmes d'analyse statique, à la suite de laquelle le modÚle de code est complété par de nouvelles informations.
- Application de rÚgles de recherche de vulnérabilités à un modÚle de code augmenté.
Différents analyseurs statiques peuvent utiliser différents modÚles de code, par exemple, le code source du programme, le flux de jetons, l'arbre d'analyse, le code à trois adresses, le graphique de flux de contrÎle, le bytecode - standard ou natif - et ainsi de suite.

Comme les compilateurs, l'analyse lexicale et syntaxique est utilisée pour construire une représentation interne, le plus souvent un arbre d'analyse (AST, Abstract Syntax Tree). L'analyse lexicale décompose le texte du programme en éléments sémantiques minimaux, à la sortie recevant un flux de jetons. L'analyse vérifie que le flux de jetons correspond à la grammaire du langage de programmation, c'est-à -dire que le flux de jetons résultant est correct du point de vue du langage. à la suite de l'analyse, un arbre d'analyse est construit - une structure qui modélise le code source du programme. Ensuite, l'analyse sémantique est appliquée; elle vérifie le respect de conditions plus complexes, par exemple, la correspondance des types de données dans les instructions d'affectation.
L'arbre d'analyse peut ĂȘtre utilisĂ© comme reprĂ©sentation interne. Vous pouvez Ă©galement obtenir d'autres modĂšles Ă partir de l'arbre d'analyse. Par exemple, vous pouvez le traduire en un code Ă trois adresses, qui, Ă son tour, crĂ©e un graphique de flux de contrĂŽle (CFG). En rĂšgle gĂ©nĂ©rale, CFG est le modĂšle principal pour les algorithmes d'analyse statique.

En analyse binaire (analyse statique de code binaire ou exĂ©cutable), un modĂšle est Ă©galement construit, mais des pratiques de reverse engineering sont dĂ©jĂ utilisĂ©es ici: dĂ©compilation, dĂ©sobfuscation, traduction inverse. Par consĂ©quent, vous pouvez obtenir les mĂȘmes modĂšles qu'Ă partir du code source, y compris le code source (en utilisant la dĂ©compilation complĂšte). Parfois, le code binaire lui-mĂȘme peut servir de reprĂ©sentation intermĂ©diaire.
ThĂ©oriquement, plus le modĂšle est proche du code source, plus la qualitĂ© de l'analyse est mauvaise. Sur le code source lui-mĂȘme, vous ne pouvez rechercher que des expressions rĂ©guliĂšres, ce qui ne vous permettra pas de trouver au moins une vulnĂ©rabilitĂ© compliquĂ©e.
Analyse du flux de données
L'un des principaux algorithmes d'analyse statique est l'analyse du flux de donnĂ©es. La tĂąche de cette analyse est de dĂ©terminer Ă chaque point du programme des informations sur les donnĂ©es sur lesquelles le code fonctionne. Les informations peuvent ĂȘtre diffĂ©rentes, par exemple, le type ou la valeur des donnĂ©es. Selon les informations Ă dĂ©terminer, la tĂąche d'analyse du flux de donnĂ©es peut ĂȘtre formulĂ©e.

Par exemple, s'il est nĂ©cessaire de dĂ©terminer si une expression est une constante, ainsi que la valeur de cette constante, le problĂšme de propagation des constantes (propagation constante) est rĂ©solu. S'il est nĂ©cessaire de dĂ©terminer le type d'une variable, alors nous pouvons parler du problĂšme de propagation de type. Si vous avez besoin de comprendre quelles variables peuvent pointer vers une zone de mĂ©moire spĂ©cifique (stocker les mĂȘmes donnĂ©es), alors nous parlons de la tĂąche de l'analyse des synonymes (analyse des alias). Il existe de nombreuses autres tĂąches d'analyse de flux de donnĂ©es qui peuvent ĂȘtre utilisĂ©es dans un analyseur statique. Comme les Ă©tapes de construction d'un modĂšle de code, ces tĂąches sont Ă©galement utilisĂ©es dans les compilateurs.
Dans la thĂ©orie de la construction de compilateurs, des solutions au problĂšme de l'analyse intra-procĂ©durale d'un flux de donnĂ©es sont dĂ©crites (il est nĂ©cessaire de suivre les donnĂ©es dans une seule procĂ©dure / fonction / mĂ©thode). Les dĂ©cisions sont basĂ©es sur la thĂ©orie des rĂ©seaux algĂ©briques et d'autres Ă©lĂ©ments des thĂ©ories mathĂ©matiques. Le problĂšme de l'analyse du flux de donnĂ©es peut ĂȘtre rĂ©solu en temps polynomial, c'est-Ă -dire pendant un temps acceptable pour les ordinateurs, si les conditions du problĂšme satisfont aux conditions du thĂ©orĂšme de solvabilitĂ©, ce qui en pratique ne se produit pas toujours.
Nous vous en dirons plus sur la rĂ©solution du problĂšme de l'analyse intra-procĂ©durale du flux de donnĂ©es. Pour dĂ©finir une tĂąche spĂ©cifique, en plus de dĂ©terminer les informations dont vous avez besoin, vous devez dĂ©terminer les rĂšgles de modification de ces informations lors de la transmission de donnĂ©es conformĂ©ment aux instructions de CFG. Rappelons que les nĆuds du CFG sont les blocs de base - des ensembles d'instructions dont l'exĂ©cution est toujours sĂ©quentielle, et les arcs indiquent le transfert de contrĂŽle possible entre les blocs de base.
Pour chaque instruction
les ensembles sont définis:
- (informations générées par l'instruction ),
- (informations détruites par l'instruction ),
- (informations au point avant l'instruction ),
- (informations au point aprĂšs l'instruction )
L'analyse de flux de données a pour objectif de définir des ensembles
et
pour chaque instruction
programmes. Le systÚme de base d'équations avec lequel les tùches d'analyse du flux de données sont résolues est déterminé par la relation suivante (équations de flux de données):
La deuxiÚme relation formule les rÚgles selon lesquelles l'information est «combinée» aux points de confluence des arcs CFG (
- prédécesseurs
en CFG). L'opĂ©ration d'union, d'intersection et quelques autres peut ĂȘtre utilisĂ©e.
L'information souhaitée (l'ensemble des valeurs des fonctions présentées ci-dessus) est formalisée comme un réseau algébrique. Les fonctions
et
sont considérés comme des mappages monotones sur des réseaux (fonctions d'écoulement). Pour les équations de flux de données, la solution est le point fixe de ces mappages.
Les algorithmes pour rĂ©soudre les problĂšmes d'analyse de flux de donnĂ©es recherchent des points fixes maximaux. Il existe plusieurs approches de la solution: algorithmes itĂ©ratifs, analyse de composants fortement connectĂ©s, analyse T1-T2, analyse d'intervalle, analyse structurelle, etc. Il existe des thĂ©orĂšmes sur l'exactitude de ces algorithmes; ils dĂ©terminent la portĂ©e de leur applicabilitĂ© Ă des problĂšmes rĂ©els. Je le rĂ©pĂšte, les conditions des thĂ©orĂšmes peuvent ne pas ĂȘtre remplies, ce qui conduit Ă des algorithmes plus compliquĂ©s et Ă des rĂ©sultats inexacts.
Analyse interprocédurale
En pratique, il est nécessaire de résoudre les problÚmes d'analyse interprocédurale du flux de données, car rarement la vulnérabilité sera complÚtement localisée dans une seule fonction. Il existe plusieurs algorithmes courants.
Fonctions en ligne . Au point d'appel de fonction, nous intégrons la fonction appelée, réduisant ainsi la tùche d'analyse interprocédurale à la tùche d'analyse intraprocédurale. Cette méthode est facilement implémentée, mais en pratique, lorsqu'elle est appliquée, une explosion combinatoire est rapidement réalisée.
Construction d'un graphique général du flux de contrÎle de programme dans lequel les appels de fonction sont remplacés par des transitions vers l'adresse de début de la fonction appelée et les instructions de retour sont remplacées par des transitions vers toutes les instructions suivant toutes les instructions pour appeler cette fonction. Cette approche ajoute un grand nombre de chemins d'exécution irréalisables, ce qui réduit considérablement la précision de l'analyse.
Un algorithme similaire au précédent, mais lors du passage à une fonction,
le contexte est
enregistré - par exemple, un cadre de pile. Ainsi, le problÚme de la création de chemins irréalisables est résolu. Cependant, l'algorithme est applicable avec une profondeur d'appel limitée.
Informations sur la fonction de construction (rĂ©sumĂ© de la fonction). L'algorithme d'analyse interprocĂ©durale le plus applicable. Il est basĂ© sur la construction d'un rĂ©sumĂ© pour chaque fonction: les rĂšgles par lesquelles les informations sur les donnĂ©es sont converties lors de l'application de cette fonction, en fonction des diffĂ©rentes valeurs des arguments d'entrĂ©e. Des rĂ©sumĂ©s prĂȘts Ă l'emploi sont utilisĂ©s dans l'analyse procĂ©durale interne des fonctions. Une difficultĂ© distincte ici est la dĂ©termination de l'ordre de la traversĂ©e de fonction, car dans l'analyse au cas par cas, un rĂ©sumĂ© doit dĂ©jĂ ĂȘtre construit pour toutes les fonctions appelĂ©es. Habituellement, des algorithmes itĂ©ratifs spĂ©ciaux pour traverser un graphe d'appel sont créés.
L'analyse interprocédurale des flux de données est une tùche exponentiellement difficile, c'est pourquoi l'analyseur doit effectuer un certain nombre d'optimisations et d'hypothÚses (il est impossible de trouver la solution exacte en temps voulu pour la puissance de calcul). Habituellement, lors du développement d'un analyseur, il est nécessaire de trouver un compromis entre la quantité de ressources consommées, le temps d'analyse, le nombre de faux positifs et les vulnérabilités trouvées. Par conséquent, un analyseur statique peut fonctionner pendant une longue période, consommer beaucoup de ressources et donner des faux positifs. Cependant, sans cela, il est impossible de trouver les vulnérabilités les plus importantes.
C'est Ă ce stade que les analyseurs statiques sĂ©rieux diffĂšrent de nombreux outils ouverts, qui, en particulier, peuvent se positionner dans la recherche de vulnĂ©rabilitĂ©s. Les vĂ©rifications rapides en temps linĂ©aire sont bonnes lorsque vous devez obtenir le rĂ©sultat rapidement, par exemple lors de la compilation. Cependant, cette approche ne peut pas trouver les vulnĂ©rabilitĂ©s les plus critiques - par exemple, liĂ©es Ă la mise en Ćuvre des donnĂ©es.
Analyse des souillures
Nous devons Ă©galement nous attarder sur l'une des tĂąches de l'analyse des flux de donnĂ©es - l'analyse des souillures. L'analyse des souillures vous permet de distribuer des drapeaux tout au long du programme. Cette tĂąche est essentielle Ă la sĂ©curitĂ© de l'information, car c'est Ă l'aide de celle-ci que des vulnĂ©rabilitĂ©s sont dĂ©couvertes liĂ©es Ă la mise en Ćuvre des donnĂ©es (injections SQL, crossite scripting, redirections ouvertes, falsification du chemin du fichier, etc.), ainsi que des fuites de donnĂ©es confidentielles (saisie du mot de passe dans journaux d'Ă©vĂ©nements, transfert de donnĂ©es non sĂ©curisĂ©).
Essayons de simuler une tĂąche. Supposons que nous voulons suivre n drapeaux -
. Beaucoup d'informations ici seront beaucoup de sous-ensembles
\ {f_1, ..., f_n \} , car pour chaque variable à chaque point du programme, nous voulons définir ses drapeaux.
Ensuite, nous devons dĂ©finir les fonctions de flux. Dans ce cas, les fonctions de flux peuvent ĂȘtre dĂ©terminĂ©es par les considĂ©rations suivantes.
- De nombreuses rÚgles sont données dans lesquelles sont définies les constructions qui conduisent à l'apparition ou au changement d'un ensemble de drapeaux.
- L'opération d'affectation fait basculer les drapeaux de droite à gauche.
- Toute opération inconnue pour les ensembles de rÚgles combine des indicateurs de tous les opérandes et l'ensemble final d'indicateurs est ajouté aux résultats de l'opération.
Enfin, vous devez dĂ©finir les rĂšgles de fusion des informations aux points de jonction des arcs CFG. La fusion est dĂ©terminĂ©e par la rĂšgle d'union, c'est-Ă -dire que si diffĂ©rents ensembles d'indicateurs pour une seule variable provenaient de diffĂ©rents blocs de base, ils sont fusionnĂ©s lors de la fusion. Y compris les faux positifs viennent d'ici: l'algorithme ne garantit pas que le chemin vers le CFG sur lequel l'indicateur est apparu peut ĂȘtre exĂ©cutĂ©.
Par exemple, vous devez détecter des vulnérabilités telles que l'injection SQL. Cette vulnérabilité se produit lorsque des données non vérifiées de l'utilisateur entrent dans les méthodes de travail avec la base de données. Il est nécessaire de déterminer que les données proviennent de l'utilisateur et d'ajouter l'indicateur de souillure à ces données. En rÚgle générale, la base de rÚgles de l'analyseur définit les rÚgles de définition de l'indicateur de souillure. Par exemple, définissez un indicateur sur la valeur de retour de la méthode getParameter () de la classe Request.

Ensuite, vous devez distribuer l'indicateur dans tout le programme analysĂ© Ă l'aide de l'analyse des souillures, Ă©tant donnĂ© que les donnĂ©es peuvent ĂȘtre validĂ©es et l'indicateur peut disparaĂźtre sur l'un des chemins d'exĂ©cution. L'analyseur dĂ©finit de nombreuses fonctions qui suppriment les indicateurs. Par exemple, la fonction de validation des donnĂ©es des balises html peut effacer l'indicateur des vulnĂ©rabilitĂ©s de script intersite (XSS). Ou, la fonction pour lier une variable Ă une expression SQL supprime l'indicateur d'intĂ©gration dans SQL.
RÚgles de recherche de vulnérabilité
Du fait de l'application des algorithmes ci-dessus, la représentation intermédiaire est complétée par les informations nécessaires à la recherche de vulnérabilités. Par exemple, dans le modÚle de code, des informations apparaissent sur les variables qui appartiennent à certains indicateurs, quelles données sont constantes. Les rÚgles de recherche de vulnérabilité sont formulées en termes de modÚle de code. Les rÚgles décrivent les fonctionnalités de la vue intermédiaire finale qui peuvent indiquer une vulnérabilité.
Par exemple, vous pouvez appliquer une rĂšgle de recherche de vulnĂ©rabilitĂ© qui dĂ©finit un appel de mĂ©thode avec un paramĂštre qui a l'indicateur taint. Revenant Ă l'exemple de l'injection SQL, nous vĂ©rifions que les variables avec le drapeau taint ne tombent pas dans la fonction de requĂȘte de base de donnĂ©es.
Il s'avÚre qu'une partie importante de l'analyseur statique, en plus de la qualité des algorithmes, est la configuration et la base de rÚgles: une description des constructions dans le code génÚre des indicateurs ou d'autres informations, des constructions qui valident de telles données et pour quelles constructions l'utilisation de ces données est critique.
D'autres approches
En plus de l'analyse du flux de données, il existe d'autres approches. L'une des plus célÚbres est l'exécution symbolique ou l'interprétation abstraite. Dans ces approches, le programme s'exécute sur des domaines abstraits, le calcul et la distribution des restrictions de données dans le programme. En utilisant cette approche, on peut non seulement trouver la vulnérabilité, mais aussi calculer les conditions sur les données d'entrée dans lesquelles la vulnérabilité est exploitable. Cependant, cette approche présente de sérieux inconvénients - avec des solutions standard sur des programmes réels, les algorithmes explosent de façon exponentielle et les optimisations entraßnent de graves pertes de qualité de l'analyse.
Conclusions
En fin de compte, je pense qu'il vaut la peine de résumer, en parlant des avantages et des inconvénients de l'analyse statique. Il est logique de comparer avec l'analyse dynamique, dans laquelle la recherche de vulnérabilité se produit lors de l'exécution du programme.

L'avantage incontestable de l'analyse statique est la couverture complĂšte du code analysĂ©. De plus, les avantages de l'analyse statique incluent le fait que pour l'exĂ©cuter, il n'est pas nĂ©cessaire d'exĂ©cuter l'application dans un environnement de combat. L'analyse statique peut ĂȘtre mise en Ćuvre aux tout premiers stades de dĂ©veloppement, minimisant ainsi le coĂ»t des vulnĂ©rabilitĂ©s trouvĂ©es.
Les inconvénients de l'analyse statique sont la présence inévitable de faux positifs, la consommation de ressources et un long temps d'analyse sur de grandes quantités de code. Cependant, ces inconvénients sont inévitables, en fonction des spécificités des algorithmes. Comme nous l'avons vu, un analyseur rapide ne trouvera jamais une véritable vulnérabilité telle que l'injection SQL et similaires.
Nous avons écrit dans
un autre article sur les difficultĂ©s restantes de l'utilisation d'outils d'analyse statique, qui, en fin de compte, peuvent ĂȘtre assez bien surmontĂ©es.