TL;DR
Les modèles de raisonnement ne sont pas nécessaires pour la plupart SAST , mais pour les cas limites tels que le parcours de chemin dans JavaScript, ils détectent deux fois plus de faux positifs.
Les modèles de raisonnement sont-ils tous exagérés ?
Les « modèles de raisonnement » ont le vent en poupe. Les grands laboratoires d'IA se livrent une bataille acharnée pour la suprématie, repoussant les limites de la taille et des performances des modèles grâce à des lois d'échelle, un pré-entraînement plus intelligent et un réglage fin avec RLHF (Reinforcement Learning from Human Feedback, apprentissage par renforcement à partir du retour d'information humain). Ils ajoutent également des couches de chaîne de pensée pour amener les modèles à « penser à voix haute » pendant l'inférence. Cela leur permet de dominer les systèmes non basés sur l'IA dans les tâches logiques et de se hisser en tête des classements.
C'est impressionnant. Mais sont-ils vraiment utiles dans la pratique ? Cela dépend.
Dans le cas d'AutoTriage*, cela dépend en grande partie de la complexité de la règle SAST.
*Petit rappel - AutoTriage est une fonctionnalité Aikido par Aikido pour filtrer les faux positifs SAST .
**SAST est une vulnérabilité potentielle découverte dans le code source, signalée par un détecteur de modèles codés en dur.
Trop cher pour la plupart SAST
AutoTriage fonctionne en deux étapes : nous essayons d'abord d'écarter la possibilité d'exploitabilité. Si cela est possible, nous pouvons filtrer les faux positifs. Cela nécessite une réflexion en noir et blanc sur l'accessibilité des variables contrôlées par l'utilisateur dans les vulnérabilités. Le modèle vérifie essentiellement si la variable est réellement contrôlée par l'utilisateur et s'il existe un système de nettoyage/validation/conversion. Et s'il existe une forme d'atténuation, il détermine si elle est réellement efficace.
La deuxième étape n'intervient que lorsque nous ne pouvons pas exclure la possibilité d'exploitation lors de la première étape. Nous nous concentrerons alors sur la hiérarchisation. La priorité est définie par la probabilité qu'un problème survienne et par la gravité de ce problème s'il venait à se produire. Cette deuxième étape est moins tranchée, mais dépend également d'estimations subjectives, par exemple lorsqu'une variable est contrôlée par l'utilisateur alors que nous ne disposons pas d'un contexte complet.
Pour la plupart des règles, nous pouvons résumer la manière de procéder en un nombre raisonnable de « règles empiriques », rendant les modèles de raisonnement superflus : ceux-ci ont généralement une précision similaire, mais leur coût est nettement plus élevé.
Pourquoi les modèles de raisonnement à petite échelle fonctionnent
Certaines règles sont étonnamment complexes et les modèles non raisonnants ont du mal à les comprendre. Imaginez que vous utilisiez exactement le même espace mental pour chaque mot que vous prononcez : au départ, quelqu'un vous demande : « combien font 1 + 1 ? » Puis, cette même personne vous demande « combien font 26248 + 346237 ? » Alors que les modèles normaux ont du mal à gérer les différents niveaux de complexité, les modèles raisonnables peuvent les traiter en utilisant simplement plus de mots pour les entrées complexes et en décomposant les problèmes plus importants en sous-problèmes plus petits et plus faciles à gérer.
Malheureusement, comme ils consomment plus de jetons, ils sont généralement aussi plus coûteux. Cependant, les modèles structurés comme des modèles de raisonnement souffrent moins de la réduction de taille que les modèles non raisonnants. Il y a deux raisons pour lesquelles on s'oriente vers des modèles plus grands : (1) ils ont une plus grande capacité de stockage de connaissances (mais il n'est pas vraiment nécessaire d'avoir beaucoup de connaissances dans le cas du triage des vulnérabilités). (2) Les modèles plus grands ont tendance à être un peu plus précis par mot. Cependant, les modèles de raisonnement peuvent se remettre de leurs erreurs grâce à leur structure de raisonnement. Ainsi, malgré une consommation plus importante de jetons, il est possible dans la pratique de travailler avec des modèles plus petits dont le coût par jeton est inférieur afin de compenser l'utilisation plus importante de jetons.
Parcours de chemin dans Javascript
Le parcours de chemin est une règle où les modèles de raisonnement peuvent vraiment briller, car ils sont étonnamment complexes à trier. Le parcours de chemin est une vulnérabilité qui permet aux utilisateurs finaux de lire ou d'écrire des fichiers en dehors d'un répertoire prévu à cet effet. Par exemple, imaginez que Google Drive dispose d'un dossier dédié à chaque utilisateur séparément sur un système de fichiers comme celui-ci :
GoogleDrive/userId1/
Google Drive/userId2/…La prochaine fois que vous souhaitez télécharger l'un de vos fichiers, vous envoyez une requête GET depuis votre navigateur client vers Google Drive, par exemple avec le nom de fichier monChienMangeDesChaussures.jpg. Si ce fichier existe, votre téléchargement commencera immédiatement. Mais que se passe-t-il si vous essayez le nom de fichier suivant : ../userId2/mesmotsdepasse.txtSi Google Drive n'avait pas protégé son back-end contre le traversal de chemin, vous auriez peut-être pu télécharger un fichier «mesmotsdepasse.txt» d'un autre utilisateur, si ce fichier existe.
Différentes attaques par parcours de chemin
Afin de trier SAST relatifs au parcours de chemin, nous devons comprendre les différents cas où quelque chose est vulnérable ou non. Commençons par les cas simples et augmentons progressivement la complexité.
Modèle 1 : « ../ »
Le problème évident ici est le motif « ../ ». Si vous lisez ou écrivez dans un chemin d'accès contenant « ../ », cela pourrait escape répertoire prévu et lire/écrire à un endroit que vous n'aviez pas prévu. Ainsi, s'il n'y a pas de vérification de « ../ » dans le chemin d'accès et que le fichier est spécifié côté client, vous avez une réelle vulnérabilité. Dans les cas les plus graves, les pirates pourraient lire les fichiers contenant les informations d'identification sur votre système.
Modèle 2 : « ..\\ »
Imaginez que vous ayez vérifié « ../ », mais que le code s'exécute sur un système Windows. Vous auriez à nouveau un problème, car le cheminement est toujours possible avec les modèles « ..\\ ». Jusqu'ici tout va bien, deux règles empiriques à vérifier, c'est encore gérable, n'est-ce pas ?
Modèle 3 : « .. »
Afin d'obtenir des chemins propres et nets sans omettre de barres obliques, beaucoup de gens utilisent des fonctions telles que path.resolve() ou path.join()C'est là que les choses deviennent intéressantes. Imaginez quelque chose comme ceci :
if (userControlledSubPath.includes(‘../’) || userControlledSubPath.includes(‘..\\’)|| filename.includes(‘../’) || filename.includes(‘..\\’))
{
throw new Error(‘Path traversal attempt detected);
}
const filepath = path.join(HARDCODED_BASE_PATH, userControlledSubPath, filename);
return fs.readFileSync(filepath);Il s'avère que cela reste vulnérable : si un pirate utilise userControlledSubPath === '..', le chemin.joindre l'interprètera toujours comme remontant d'un répertoire.
Cependant, le dernier argument dans path.join() est immunisé contre cette attaque. Si un attaquant spécifie « .. » dans le dernier argument, le path.join() La fonction renverrait un répertoire au lieu d'un chemin d'accès au fichier, ce qui entraînerait une opération de lecture/écriture non valide.
Modèle 4 : « /* »
Dans un nouvel exemple, nous avons à nouveau eu un test comme celui-ci :
if (filename.includes(‘..’))
{
throw new Error(‘Path traversal attempt detected);
}
const filepath = path.resolve(HARDCODED_BASE_PATH, filename);
return fs.readFileSync(filepath);Cela semble sûr, n'est-ce pas ? La vérification couvre les cas « .. », « ../ » et « ..\\ » - c'est élégant ! Mais voici maintenant la manière surprenante dont cela reste vulnérable. Roulement de tambour... trrrrrrrrr... Lorsqu'un argument dans path.resolve() commence par une barre oblique, il ignore tous les arguments précédents. Ainsi, lorsqu'un pirate fait quelque chose comme filename = /etc/passwd, alors path.resolve() ignorera le chemin de base codé en dur et le résoudra en /etc/passwd. Effrayant, n'est-ce pas ? Nous aurions dû vérifier également la présence de cette barre oblique finale. Notez que l'utilisation de path.join() aurait rendu cela sûr.
Apprécier la complexité
Charlie Chaplin a dit un jour : « La simplicité n'est pas une chose simple ». Cela s'applique également ici : il existe des solutions simples et efficaces, mais vous devez d'abord comprendre l'éventail des vecteurs d'attaque possibles. La solution la plus simple et la plus robuste contre le traversalage de chemin consiste à résoudre d'abord le chemin et à vérifier s'il commence toujours par le chemin de base prévu. Il n'y a aucun moyen d'échapper à cette vérification.
Cependant, l'équipe AutoTriage n'a pas le luxe de pouvoir choisir la solution corrective. Nous devons être en mesure de marquer les solutions alternatives comme sûres afin de ne pas submerger inutilement les clients avec des faux positifs. Nous avons désormais identifié quatre vecteurs d'attaque différents liés au cheminement de chemin, qui sont tous assortis de vérifications spécifiques. Pour chacun de ces vecteurs d'attaque, le LLM doit vérifier s'il peut se produire en remplissant toutes les conditions nécessaires pour mener à bien une attaque ou pour exclure toute possibilité d'attaque.
Bien que les modèles de raisonnement ne soient pas la norme pour la plupart des règles, ils sont capables de filtrer en toute sécurité deux fois plus de faux positifs que les modèles sans raisonnement pour le parcours de chemin dans JavaScript. Cela change la donne en matière de réduction du bruit.
Sécurisez votre logiciel dès maintenant.



.avif)
