Python a une longue histoire d'être impossible à créer un bac à sable sécurisé (voir Comment puis-je sandbox Python en pur Python? comme point de départ, puis plongez dans une ancienne discussion python-dev si cela vous intéresse). Voici ce que je considère comme vos deux meilleures options.
Pré-analysez le code
Avant d'exécuter quoi que ce soit, analysez le code. Vous pourriez le faire en Python avec le module AST puis parcourir l'arbre, ou vous pourriez probablement vous en sortir avec des recherches de texte plus simples. Cela fonctionne probablement dans votre scénario parce que vous avez des cas d'utilisation restreints - cela ne se généralise pas à du code vraiment arbitraire.
Ce que vous cherchez dans votre cas sera n'importe quelle instruction import
(simple), et toute variable de niveau supérieur (par exemple, dans a.b.c
vous vous intéresserez à a
et probablement a.b
pour un certain a
) qui ne sont pas "approuvées". Cela vous permettra d'échouer sur tout code qui n'est pas propre avant de l'exécuter.
Le défi ici est que même un code triviallement obscurci contournera vos vérifications. Par exemple, voici quelques façons d'importer des modules en utilisant d'autres modules ou des globales qu'une analyse de base pour import
ne trouvera pas. Vous voudriez probablement restreindre l'accès direct à __builtins__
, globals
, certains/la plupart/tous les noms avec __double_underscores__
et les membres de certains types. Dans un AST, ceux-ci apparaîtront inévitablement comme des lectures de variables de niveau supérieur ou des accès d'attributs.
getattr(__builtins__, '__imp'+'ort__')('autre_module')
globals()['__imp'+'ort__']('autre_module')
module.__loader__.__class__(
"autre_module",
module.__loader__.path + '/../autre_module.py'
).load_module()
(J'espère que cela va un peu sans dire, c'est un défi impossible, et c'est pourquoi cette approche de bac à sable n'a jamais pleinement réussi. Mais cela peut être suffisant, en fonction de votre modèle de menace spécifique.)
Audit en temps d'exécution
Si vous êtes en mesure de compiler votre propre exécution Python, vous pourriez envisager d'utiliser les crochets (actuellement en brouillon) du PEP 551. (Avis de non-responsabilité: je suis l'auteur de ce PEP.) Il existe des implémentations en brouillon contre les dernières versions 3.7 et 3.6.
Essentiellement, cela vous permettrait d'ajouter des crochets pour toute une série d'événements au sein de Python et de déterminer comment y répondre. Par exemple, vous pouvez écouter tous les événements import
et déterminer si vous devez les autoriser ou les refuser à l'exécution en fonction du module exact qui est importé, ou écouter les événements compile
pour gérer toutes les compilations à l'exécution. Vous pouvez le faire en code Python (avec sys.addaudithook
) ou en code C (avec PySys_AddAuditHook
).
Le fichier Programs/spython.c dans le dépôt est un exemple assez complet d'audit en C, tandis que le faire en Python ressemble plus à ceci (extrait de ma présentation sur ce PEP):
import sys
def empecher_bitly(evenement, args):
if evenement == 'urllib.Request' et '://bit.ly/' in args[0]:
print(f'Avertissement: urlopen({args[0]}) bloqué')
raise RuntimeError('l'accès à bit.ly n'est pas autorisé')
sys.addaudithook(empecher_bitly)
L'inconvénient de cette approche est que vous devez construire et distribuer votre propre version de Python, au lieu de vous fier à une installation système. Cependant, en général, c'est une bonne idée si votre application dépend de l'intégration, car cela signifie que vous n'aurez pas à contraindre les utilisateurs dans une configuration système spécifique.