有没有办法以编程方式限制 python 对存储的访问?

Is there a way to limit python's access to storage programmatically?

所以我有一些未经验证的脚本,这些脚本是动态生成的并且需要 运行。所以我不得不限制对模块的访问,在我的例子中是 ioosshutil 等...

现在这是我尝试过的东西

from types import ModuleType

def generate_empty_globals():
    return ModuleType("__main__").__dict__


module_locker = """
import os, sys, io
def empty_module(module):
    for x in dir(module):
        setattr(module, x, None)

empty_module(os)
empty_module(sys)
empty_module(io)
"""
my_module = generate_empty_globals()

code = """ ... """
exec(module_locker, my_module)
exec(code, my_module)

现在在上面的方法中,我已经导入模块并将其中的每个函数变量设置为 None,这样如果未知代码 运行s 并导入库,库将不会重新导入,因为库已经导入,这提供了良好的安全级别,但同时你有这样的代码

import os 
import importlib

importlib.reload(os)

从而允许他们再次访问模块。所以我想我可以通过阻止 importlib 本身的重新加载方法来阻止它们,但不幸的是,给出的一些代码依赖于重新加载能力。现在我有了一个新想法,基本上是使用 ast 来修改代码结构并删除那些 import 语句,但我不知道该怎么做,所以有没有不依赖平台的方法具体方法。

编辑

只是一种限制模块访问的方法(仅)。为了模块的允许,我将使用一个特殊的模块或者说 api.

首先让我说,我认为您真的应该考虑替代方案,例如使用子进程 运行 作为更受限制的用户。如果你的手被束缚并且由于某种原因你被迫以这种方式实现它,你可能想要查看 importlib 挂钩(在你继续记录说明使用导入挂钩解决问题是一个坏主意之后)。

来自PEP 302 - New Import Hooks

There are two types of import hooks: Meta hooks and Path hooks. Meta hooks are called at the start of import processing, before any other import processing (so that meta hooks can override sys.path processing, frozen modules, or even built-in modules). To register a meta hook, simply add the finder object to sys.meta_path (the list of registered meta hooks.)

importlib.abc.MetaPathFinder

An abstract base class representing a meta path finder. For compatibility, this is a subclass of Finder.

New in version 3.3.

find_spec(fullname, path, target=None)

An abstract method for finding a spec for the specified module. If this is a top-level import, path will be None. Otherwise, this is a search for a subpackage or module and path will be the value of path from the parent package. If a spec cannot be found, None is returned. When passed in, target is a module object that the finder may use to make a more educated guess about what spec to return. importlib.util.spec_from_loader() may be useful for implementing concrete MetaPathFinders.

New in version 3.4.

find_module(fullname, path)

A legacy method for finding a loader for the specified module. If this is a top-level import, path will be None. Otherwise, this is a search for a subpackage or module and path will be the value of path from the parent package. If a loader cannot be found, None is returned.

If find_spec() is defined, backwards-compatible functionality is provided.

Changed in version 3.4: Returns None when called instead of raising NotImplementedError. Can use find_spec() to provide functionality.

Deprecated since version 3.4: Use find_spec() instead.

invalidate_caches()

An optional method which, when called, should invalidate any internal cache used by the finder. Used by importlib.invalidate_caches() when invalidating the caches of all finders on sys.meta_path.

Changed in version 3.4: Returns None when called instead of NotImplemented.

只是稍微尝试了一下,我就能够制作一个导入挂钩,用于检查请求的模块是否在已批准模块列表中。如果是,它将使用默认导入器导入模块。如果不是,则会引发错误。

我可以用这样的解决方案想到两个主要问题(肯定还有更多)。首先,一个顶级模块可以导入几十个其他模块。其次,如果您需要允许脚本导入 sys 或 importlib,他们可以轻松找到绕过导入限制的方法。

import sys
import importlib

class MyImporter():
    original_importers = sys.meta_path
    approved = ['os']
    
    @classmethod
    def find_spec(cls, fullname, path, target=None):
        if fullname not in cls.approved:
            raise RuntimeError('Permission for module {} not approved'.format(fullname))
        for importer in cls.original_importers:
            spec =  importer.find_spec(fullname, path, target)
            if spec is not None:
                return spec
        return None

if __name__ == '__main__':
    importlib.abc.MetaPathFinder.register(MyImporter)
    sys.meta_path = [MyImporter]
    import os
    import argparse # fails because argparse is not in approved modules