Auditer les lacunes de garde : le pair non surveillé est le bogue
La plupart des bogues de contrôle d'accès et de validation manquante ne sont pas exotiques - ce sont un
gestionnaire dans une famille qui a oublié la vérification que ses frères et sœurs effectuent. Le
admin_delete qui vérifie un rôle et le bulk_delete qui ne le fait pas. Le
read_bounded qui valide la longueur et le read_fast qui lui fait confiance. Lire les
fichiers de haut en bas cache ces choses ; comparer les pairs les révèle. C'est l'une des
tactiques les plus rentables en boîte blanche.
L'asymétrie que vous traquez
garde → puits (le chemin sûr et intentionnel)
??? → puits (un chemin pair avec la garde manquante - le bogue)
Quand l'utiliser
- Un puits est accessible depuis plusieurs sites d'appel et vous soupçonnez que l'un d'eux saute une vérification.
- Vous auditez un modèle d'autorisation, une famille d'analyseurs syntaxiques ou un groupe de gestionnaires pour une vérification oubliée.
- Une constatation a besoin que son « pourquoi c'est faux » soit formulé comme une différence concrète par rapport à un pair correct.
Vérification du périmètre
Source autorisée uniquement. Si vous ne pouvez pas nommer l'autorisation, arrêtez-vous.
La boucle
-
Choisissez une ancre gardée. Trouvez une fonction qui fait une validation avant un puits sensible - une vérification d'autorisation/propriété, une vérification de bornes, une liste blanche, un assainisseur. C'est votre référence pour « ce qui est correct ici ».
-
Épinglez la garde au puits qu'elle protège. Nommez la vérification exacte et le puits exact. « Correct » signifie que la garde domine le puits : sur chaque chemin à travers l'ancre, la vérification s'exécute avant le puits. La simple présence dans la fonction n'est pas une domination.
-
Énumérez les pairs structurels. Élargissez au-delà des voisins du même fichier - le point est la parenté structurelle : les fonctions qui atteignent le même puits ; les surcharges de la même interface/méthode virtuelle ; les gestionnaires dans la même table de routage / table de répartition / commutateur de commandes ; les membres parallèles d'une classe (
get_x/set_x/delete_x) ; les appelants d'un assistant partagé où certains assainissent l'argument et d'autres le passent brut. Les pairs qui atteignent le même puits sont le signal le plus fort. -
Comparez chaque pair à l'ancre - par ce que la garde impose, pas par son nom. Pour chaque pair, demandez si une vérification équivalente domine le même puits. Classez l'écart :
- absente - aucune vérification équivalente du tout.
- contournable - présente mais contournable sur un certain chemin (retour anticipé, un
||court-circuitant, une branche qui saute la vérification). - plus-faible-que-le-pair - un type différent de vérification : authentification là où l'ancre fait de l'autorisation ; une vérification de type là où l'ancre fait des bornes ; un champ là où l'ancre vérifie aussi le champ frère.
- après-le-puits - la vérification s'exécute, mais après l'opération dangereuse.
- mauvais-contexte - une vraie vérification qui ne couvre pas la classe de charge utile de ce puits (un encodeur qui est incorrect pour le langage du puits).
-
Confirmez l'attaquabilité avec une entrée d'attaquant. Un puits non gardé n'est un bogue que si une entrée non fiable l'atteint. Retracez l'argument dangereux du pair jusqu'à une source réelle (remettez à
adjudicating-taint-paths). Un pair uniquement appelé avec des constantes n'est pas une constatation. -
Enregistrez comme une différence - dans le schéma. Énoncez-le comme : « l'ancre A garde le puits S avec la vérification C (dominante) ; le pair B atteint S avec
guard_status= <absente / contournable / plus-faible-que-le-pair / après-le-puits / mauvais-contexte>, et l'argument de B est contrôlé par l'attaquant via . » Émettez selon le schéma de constatation.
Exemple concret
Ancre :
delete_own_post(utilisateur, id_post)- vérifiepost.proprietaire == utilisateur.id(domine) avantdb.delete(post). Pair :admin_bulk_delete(ids)atteint le même puitsdb.delete; la seule porte est un@login_requiredau niveau de la route, aucune vérification de propriété par objet. Différence :guard_status = plus-faible-que-le-pair- authentification présente, autorisation (propriété) absente. Attaquabilité :idsvient directement du corps de la requête ; tout utilisateur authentifié l'atteint. IDOR/BFLA confirmé, gravité élevée - « tout utilisateur connecté supprime les articles de n'importe quel utilisateur par id. »
Rationalisations à rejeter
- « Il y a une vérification dans la fonction, donc c'est gardé. » → Seulement si elle domine le puits. Une vérification sur une branche que le puits ignore n'est pas une garde.
- « Les deux fonctions ont une authentification, elles sont équivalentes. » → L'authentification n'est pas l'autorisation ; une vérification de type n'est pas une vérification de bornes. Comparez ce qui est imposé.
- « Le pair semble non gardé - signalez-le. » → Pas avant d'avoir confirmé que l'entrée de l'attaquant atteint le puits. Inatteignable ≠ constatation.
- « Voisins du même fichier uniquement. » → Le pair dangereux vit souvent dans un autre module atteignant le même puits. Énumérez par structure, pas par fichier.
Exécution en pratique
Vous avez besoin d'un outillage capable de regrouper les fonctions par le puits qu'elles atteignent et d'afficher le
corps exact de chacune, afin de pouvoir comparer garde par garde et vérifier la domination. Un
graphe de propriétés de code rend « quels pairs atteignent ce puits, et lesquels valident en premier »
directement répondable ; sinon, énumérez les pairs manuellement à partir des sites d'appel du
puits partagé ou de l'assistant et lisez chacun. L'étape d'attaquabilité réutilise
adjudicating-taint-paths.
Associé
adjudicating-taint-paths- confirmer que le puits du pair est atteignable avec une entrée.hunting-bugs-with-a-code-graph- la boucle maîtresse que cela alimente.- FINDING-SCHEMA.md - la forme que prend chaque constatation.