Pysa: Comment éviter les problèmes de sécurité dans le code Python



Le 7 août, Facebook a présenté Pysa, un analyseur statique open source axé sur la sécurité qui vous aide à travailler avec des millions de chaînes Instagram. Les limites sont divulguées, les décisions de conception sont abordées et, bien sûr, les moyens permettant d'éviter les faux positifs. La situation est indiquée lorsque Pysa est le plus utile et le code dans lequel l'analyseur n'est pas applicable. Détails du blog d'ingénierie Facebook sous la coupe.



L'année dernière, nous avons décrit comment nous avons construit Zoncolan , un outil d'analyse statique qui analyse plus de 100 millions de lignes de code Hack et aide les ingénieurs à prévenir des milliers de problèmes de sécurité potentiels. Le succès a inspiré Pysa - Python Static Analyzer. L'analyseur est construit sur Pyre, l'outil de vérification de type Python de Facebook. Pysa fonctionne avec le flux de données dans le code. L'analyse des flux de données est utile car souvent les problèmes de sécurité et de confidentialité sont modélisés comme des données qui ne devraient pas être.



Pysa aide à identifier de nombreux types de problèmes. L'analyseur vérifie si le code utilise correctement certaines structures internes pour empêcher l'accès ou la divulgation des données utilisateur sur la base de politiques de confidentialité techniques. De plus, l'analyseur détecte les problèmes de sécurité courants des applications Web, tels que l'injection XSS et SQL. Comme Zoncolan, le nouvel outil a contribué à intensifier les efforts de sécurité des applications Python. Cela est particulièrement vrai pour Instagram.



Pysa sur Instagram



Le plus grand référentiel Python sur Facebook comprend des millions de lignes sur les serveurs Instagram. Lorsque Pysa est exécuté sur un changement de code suggéré par le développeur, il fournit des résultats en environ une heure, plutôt que les semaines ou les mois nécessaires pour une vérification manuelle. Cela vous aide à trouver et à prévenir un problème suffisamment rapidement pour qu'il n'entre pas dans votre base de code. Les résultats des contrôles sont envoyés directement au développeur ou aux ingénieurs de sécurité, en fonction du type de problème et du rapport signal / bruit dans la situation particulière.



Pysa et Open Source



Le code source de Pysa et de nombreuses définitions de problèmes sont ouverts aux autres développeurs pour analyser le code de leurs projets. Nous travaillons avec des frameworks côté serveur open source tels que Django et Tornado , donc dès le premier lancement dans Facebook, Pysa détecte des problèmes de sécurité dans les projets utilisant ces frameworks. Utiliser Pysa pour les frameworks qui n'ont pas encore de couverture est généralement aussi simple que d'ajouter quelques lignes de configuration. Il vous suffit d'indiquer à l'analyseur la provenance des données vers le serveur.



Pysa a été utilisé pour détecter des problèmes tels que CVE-2019-19775 dans des projets Python open source. Nous avons également travaillé avec le projet Zulip et a inclus Pysa dans sa base de code.



Comment ça fonctionne?



Pysa est conçu à partir des leçons tirées de Zoncolan. Il utilise les mêmes algorithmes pour effectuer une analyse statique et partage même du code avec Zoncolan. Comme Zoncolan, Pysa surveille le flux de données dans un programme. L'utilisateur définit les sources des données importantes et les destinations d'où proviennent les données. Dans les applications de sécurité, les types de sources les plus courants sont les points où les données contrôlées par l'utilisateur entrent dans l'application, comme le dictionnaire HttpRequest.GET dans Django. Les récepteurs sont généralement beaucoup plus variés et peuvent inclure l'exécution d'API. Par exemple, evalouos.open... Pysa effectue de manière itérative des cycles d'analyse pour créer des résumés afin de déterminer quelles fonctions renvoient des données de la source et lesquelles ont des paramètres atteignant la destination. Lorsque l'analyseur détecte que la source se connecte finalement au récepteur, il signale le problème. La visualisation de ce processus est un arbre avec un problème en haut et des sources et des flux dans les feuilles:







Pour effectuer une analyse inter-procédurale - pour suivre le flux de données entre les appels de fonction - vous devez être en mesure de mapper les appels de fonction à leurs implémentations. Pour ce faire, vous devez utiliser toutes les informations disponibles dans le code, y compris les types statiques facultatifs, le cas échéant. Nous avons travaillé avec Pyre pour trouver ces informations. Bien que Pysa s'appuie fortement sur Pyre et que les deux outils partagent le même référentiel, il est important de noter qu'il s'agit de produits distincts avec des applications distinctes. 



Faux positifs



Les ingénieurs de sécurité sont les principaux utilisateurs de Pysa sur Facebook. Comme tout ingénieur travaillant avec des outils automatisés de détection d'erreurs, nous avons dû comprendre comment gérer les faux positifs (pas de problème, pas de signal) et les négatifs (pas de problème, pas de signal).



La conception de Pysa vise à éviter de négliger les problèmes et à détecter autant de problèmes réels que possible. Cependant, réduire le nombre de fausses alarmes peut nécessiter des compromis qui augmentent le nombre d'alarmes inutiles. Trop de faux positifs provoquent une fatigue anxieuse et le risque que de vrais problèmes soient négligés dans le bruit. Pysa dispose de deux outils pour supprimer les signaux indésirables: les désinfectants et les panneaux.



DésinfectantEst un outil simple. Il indique à l'analyseur de ne pas suivre le flux de données une fois que le flux est passé par la fonction ou l'attribut. Les désinfectants vous permettent d'encoder les connaissances de transformation de domaine qui présentent toujours les données de manière sécurisée et confidentielle.



Les signes sont plus subtils: ce sont de petits morceaux de métadonnées que Pysa attache aux flux de données lors de son suivi. Contrairement aux désinfectants, les panneaux ne suppriment pas les problèmes des résultats d'analyse. Les attributs et autres métadonnées peuvent être utilisés pour filtrer les résultats après analyse. Les filtres sont généralement écrits pour une paire source-destination spécifique afin d'ignorer les problèmes lorsque les données ont déjà été traitées pour un type spécifique (mais pas tous les types) d'une destination.



Pour comprendre dans quelles situations Pysa est le plus utile, imaginez que le code suivant s'exécute pour charger un profil utilisateur:



# views/user.py
async def get_profile(request: HttpRequest) -> HttpResponse:
   profile = load_profile(request.GET['user_id'])
   ...
 
# controller/user.py
async def load_profile(user_id: str):
   user = load_user(user_id) # Loads a user safely; no SQL injection
   pictures = load_pictures(user.id)
   ...
 
# model/media.py
async def load_pictures(user_id: str):
   query = f"""
      SELECT *
      FROM pictures
      WHERE user_id = {user_id}
   """
   result = run_query(query)
   ...
 
# model/shared.py
async def run_query(query: str):
   connection = create_sql_connection()
   result = await connection.execute(query)
   ...


C'est là que l'injection SQL potentielle dans load_pictures ne peut pas être exploitée: cette fonction est toujours valide à user_idpartir de la fonction load_userdans load_profile. Lorsqu'il est configuré correctement, Pysa ne signalera probablement pas de problème. Imaginez maintenant qu'un ingénieur entreprenant écrivant du code au niveau du contrôleur se rende compte que la récupération des données utilisateur et d'une image en même temps renvoie des résultats plus rapidement:



# controller/user.py
async def load_profile(user_id: str):
   user, pictures = await asyncio.gather(
       load_user(user_id),
       load_pictures(user_id) # no longer 'user.id'!
   )
   ...


Le changement peut sembler inoffensif, mais il finit en fait par fusionner la chaîne contrôlée par l'utilisateur user_idavec le problème d'injection SQL dans load_pictures. Dans une application avec de nombreuses couches entre le point d'entrée et les requêtes de base de données, l'ingénieur peut ne pas se rendre compte que les données sont entièrement contrôlées par l'utilisateur, ou que le problème d'injection est caché dans la fonction appelée. C'est exactement la situation pour laquelle l'analyseur a été écrit. Lorsqu'un ingénieur propose un changement similaire sur Instagram, Pysa découvre que les données passent d'une entrée pilotée par l'utilisateur à une requête SQL et signale le problème.



Limitations de l'analyseur



Il est impossible d'écrire un analyseur statique parfait . Pysa a des limites dans la portée, le flux de données et les décisions de conception, compromettant les performances pour la précision et l'exactitude. Python en tant que langage dynamique a des caractéristiques uniques qui sous-tendent certaines de ces décisions de conception.



Espace problème



Pysa est conçu pour détecter uniquement les problèmes de sécurité liés aux flux de données. Tous les problèmes de sécurité ou de confidentialité ne sont pas modélisés sous forme de flux de données. Découvrez un exemple:



def admin_operation(request: HttpRequest):
  if not user_is_admin():
      return Http404
 
  delete_user(request.GET["user_to_delete"])


Pysa n'est pas le bon outil pour garantir qu'un contrôle d'autorisation est user_is_adminexécuté avant une opération privilégiée delete_user. L'analyseur peut détecter les données de request.GETdirigé vers delete_user, mais ces données ne sont jamais validées user_is_admin. Vous pouvez réécrire le code pour que le problème soit modélisé par Pysa, ou vous pouvez intégrer la vérification des autorisations dans une opération administrative delete_user. Mais ce code montre tout d'abord quels problèmes Pysa ne résout pas.



Limites de ressources



Nous avons pris une décision de conception sur les contraintes afin que Pysa puisse terminer l'analyse avant que les modifications proposées par les développeurs ne soient intégrées dans la base de code. Lorsque l'analyseur surveille les flux de données dans trop d'attributs d'un objet, vous devez parfois simplifier et traiter l'objet entier comme contenant exactement ces données. Cela peut conduire à de faux positifs.



Une autre limitation est le temps de développement. Cela a forcé un compromis sur les fonctionnalités Python prises en charge. Pysa n'inclut pas encore les décorateurs dans le graphe d'appel lors de l'appel de fonctions et ignore donc les problèmes à l'intérieur des décorateurs. 



Python comme langage dynamique



La flexibilité de Python rend l'analyse statique difficile. Il est difficile de suivre les flux de données via des appels de méthode sans informations de type. Dans le code ci-dessous, il est impossible de déterminer laquelle des implémentations flyest appelée:



class Bird:
  def fly(self): ...
 
class Airplane:
  def fly(self): ...
 
def take_off(x):
  x.fly()  # Which function does this call?


L'analyseur fonctionne dans des projets totalement non typés. Mais il faut peu d'efforts pour couvrir les types importants.



La nature dynamique de Python impose une autre limitation. Voir ci-dessous:



def secret_eval(request: HttpRequest):
  os = importlib.import_module("os")
 
  # Pysa won't know what 'os' is, and thus won't
  # catch this remote code execution issue
  os.system(request.GET["command"])


La vulnérabilité d'exécution est clairement visible ici, mais l'analyseur l'ignorera. Le module est osimporté dynamiquement. Pysa ne comprend pas que la variable locale os représente exactement le module os. Python vous permet d'importer dynamiquement presque n'importe quel code à tout moment. De plus, le langage peut changer le comportement d'un appel de fonction pour presque n'importe quel objet. Pysa peut apprendre à analyser le système d'exploitation et à détecter le problème. Mais le dynamisme de Python signifie qu'il existe d'innombrables exemples de flux de données pathologiques que l'analyseur ne verra pas.



résultats



Au premier semestre 2020, Pysa représentait 44% de tous les problèmes détectés sur Instagram. Parmi tous les types de vulnérabilité, 330 problèmes uniques ont été détectés dans les modifications de code proposées. 49 (15%) problèmes se sont avérés importants, 131 des problèmes (40%) étaient réels, mais avaient des circonstances atténuantes. Des faux négatifs ont été enregistrés dans 150 (45%) cas.



Nous examinons régulièrement les problèmes signalés par d'autres moyens. Par exemple, via le programme Bug Bounty. C'est ainsi que nous nous assurons de corriger tous les faux signaux négatifs. La détection de chaque type de vulnérabilité est paramétrable. Grâce à un perfectionnement constant, les ingénieurs en sécurité sont passés à des types plus sophistiqués pour signaler les problèmes réels 100% du temps.



Dans l'ensemble, nous sommes satisfaits des compromis que nous avons faits pour aider les ingénieurs de sécurité à évoluer. Mais il y a toujours place pour le développement. Nous avons créé Pysa pour améliorer continuellement la qualité du code grâce à une étroite collaboration entre les ingénieurs en sécurité et les programmeurs. Cela nous a permis d'itérer rapidement et de créer un outil qui répond mieux à nos besoins que n'importe quelle solution prête à l'emploi. La collaboration des ingénieurs a conduit à des ajouts et des raffinements aux mouvements Pysa. Par exemple, la façon dont vous affichez la trace des problèmes a changé. Il est désormais plus facile de voir les faux négatifs.



Documentation et tutoriel sur l' analyseur Pysa .

image


Découvrez comment obtenir une profession de haut niveau à partir de zéro ou augmenter vos compétences et vos salaires en suivant les cours en ligne SkillFactory:





E







All Articles