2

我写了一个主要的 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

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

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

4

3 回答 3

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 ...

至于安全性,如果您允许用户导入一些任意代码集(例如他们自己的模块,也许?),它与eval在用户输入上使用并没有太大区别。它本质上是相同的漏洞:用户可以让你的程序执行他们自己的代码。

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

于 2013-03-25T18:50:13.520 回答
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对比__IMPORT__()

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

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

白名单

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

最终,您可以将 parser_class*.py 代码与哈希列表进行比较,就像sha1sum一样。

最后的评论:实际上,如果用户对您的脚本文件夹具有写入权限,则您无法确保代码绝对安全。

于 2013-03-25T18:57:16.003 回答
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()"
于 2013-03-25T18:56:34.423 回答