有条件地加载模块Python

时间:2013-03-25 18:47:36

标签: python python-2.7

我写了一个主python模块,需要加载文件解析器才能工作,最初我只是一个文本解析器模块,但我需要为不同的情况添加更多的解析器。
parser_class1.py
parser_class2.py
parser_class3.py

每个正在运行的实例只需要一个,然后我想通过命令行加载它:

mmain.py -p parser_class1

出于这个目的,我编写了这段代码,以便在调用主模块时选择要加载的解析器:

#!/usr/bin/env python

import argparse
aparser = argparse.ArgumentParser()
aparser.add_argument('-p',
            action='store',
            dest='module',
            help='-p module to import')
results = aparser.parse_args()

if not results.module:
    aparser.error('Error! no module')
try:
    exec("import %s" %(results.module))
    print '%s imported done!'%(results.module)
except ImportError, e:
    print e

但是,我正在读这种方式很危险,也许没有标准......

然后,这种方法可以吗?或者我必须找到另一种方法来做到这一点? 为什么? 谢谢,欢迎任何评论。

3 个答案:

答案 0 :(得分:5)

实际上你可以在条件块中执行import语句:

if x:
    import module1a as module1
else:
    import module1b as module1

您可以使用此方法以不同的方式考虑各种白名单模块导入,但有效的想法是预先编程导入,然后基本上使用GOTO进行正确的导入...如果您确实想要让用户导入任意参数,然后__import__函数就可以了,而不是eval

<强>更新

正如评论中提到的@thedox,as module1部分是用不同底层代码加载类似API的惯用方法。 在您打算使用完全不同的API执行完全不同的事情的情况下,这不是要遵循的模式。 在这种情况下,更合理的模式是包含与该import语句的特定导入相关的代码:

if ...:
    import module1
    # do some stuff with module1 ...

else:
    import module2
    # do some stuff with module2 ...

至于 security ,如果你允许用户导入一些任意代码集(例如他们自己的模块,也许?),那么它与使用eval没什么不同在用户输入。它本质上是同一个漏洞:用户可以让你的程序执行自己的代码。

我认为没有一种真正安全的方式让用户导入任意模块。这里的例外情况是,如果他们无法访问文件系统,因此无法创建要导入的新代码,在这种情况下,您基本上会回到白名单案例,并且还可以实施明确的白名单以防止将来 - 如果/在将来的某个时刻用户确实获得了文件系统访问权限的漏洞。

答案 1 :(得分:1)

以下是__import__()

的使用方法
allowed_modules = ['os', 're', 'your_module', 'parser_class1.py', 'parser_class2.py']

if not results.module:
    aparser.error('Error! no module')
try:
    if results.module in allowed_modules:
        module = __import__(results.module)
        print '%s imported as "module"'%(results.module)
    else:
        print 'hey what are you trying to do?'
except ImportError, e:
    print e

module.your_function(your_data)

EVAL vs __IMPORT__()

使用eval允许用户在您的计算机上运行任何代码。不要那样做。 __import__()仅允许用户加载模块,显然不允许用户运行任意代码。但它显然更安全。

建议的函数,没有allowed_modules仍然存在风险,因为它可以允许加载任何可能在加载时运行某些恶意代码的模型。攻击者可能会在某处加载文件(共享文件夹,ftp文件夹,由您的网络服务器管理的上传文件夹......)并使用您的参数调用它。

<强>白名单

使用allowed_modules缓解了问题,但没有完全解决问题:要进一步强化,你仍然需要检查攻击者是否写了“os.py”,“re.py”,“your_module.py” ,“parser_class1.py”进入你的脚本文件夹,因为python首先在那里搜索模块(docs)。

最后,您可以将parser_class * .py代码与哈希列表进行比较,例如sha1sum

最终备注:在实际应用中,如果用户对您的脚本文件夹具有写入权限,则无法确保代码绝对安全。

答案 2 :(得分:0)

您应该考虑可以为该解析函数导入的所有可能模块,然后使用case语句或字典来加载正确的模块。例如:

import parser_class1, parser_class2, parser_class3

parser_map = {
    'class1': parser_class1,
    'class2': parser_class2,
    'class3': parser_class3,
}

if not args.module:
    #report error
    parser = None
else:
    parser = parser_map[args.module]
#perform work with parser

如果在此示例中加载任何parser_classN模块的代价很高,您可以定义返回该模块的lambda或函数(即def get_class1(): import parser_class1; return parser_class1)并将该行更改为parser = parser_map[args.module]()

exec选项可能非常危险,因为您正在执行未经验证的用户输入。想象一下,如果您的用户做了类似的事情 -

mmain.py -p "parser_class1; some_function_or_code_that_is_malicious()"