我写了一个主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
但是,我正在读这种方式很危险,也许没有标准......
然后,这种方法可以吗?或者我必须找到另一种方法来做到这一点? 为什么? 谢谢,欢迎任何评论。
答案 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()"