应用程序想要解析和“执行”一个文件,并且出于安全原因想要断言该文件是可执行的。
片刻思考,你意识到这个初始代码有一个竞争条件,使安全方案无效:
import os
class ExecutionError (Exception):
pass
def execute_file(filepath):
"""Execute serialized command inside @filepath
The file must be executable (comparable to a shell script)
>>> execute_file(__file__) # doctest: +ELLIPSIS
Traceback (most recent call last):
...
ExecutionError: ... (not executable)
"""
if not os.path.exists(filepath):
raise IOError('"%s" does not exist' % (filepath, ))
if not os.access(filepath, os.X_OK):
raise ExecutionError('No permission to run "%s" (not executable)' %
filepath)
data = open(filepath).read()
print '"Dummy execute"'
print data
竞争条件存在于
os.access(filepath, os.X_OK)
和
data = open(filepath).read()
因为在这两个系统调用之间,文件有可能被不同内容的不可执行文件覆盖。
我的第一个解决方案是更改关键调用的顺序(并跳过现在冗余的存在性检查):
fobj = open(filepath, "rb")
if not os.access(filepath, os.X_OK):
raise ExecutionError('No permission to run "%s" (not executable)' %
filepath)
data = fobj.read()
这是否解决了比赛条件?我怎样才能正确解决它?
简要说明安全方案的基本原理(我想)
该文件将能够在其环境中执行任意命令,因此它可以与 shell 脚本相媲美。
带有定义应用程序的 .desktop 文件的免费桌面存在安全漏洞:该文件可以指定任何带有参数的可执行文件,并且可以选择自己的图标和名称。因此,随机下载的文件可以隐藏在任何名称或图标后面并执行任何操作。那很糟糕。
这通过要求 .desktop 文件设置可执行位来解决,否则它们将不会以名称/图标呈现,并且免费桌面会询问用户是否要在开始之前启动程序。
对比一下 Mac OS X 的非常好的设计:“这个程序已经从网上下载了,你确定要打开它吗?”。
因此,在这个寓言中,以及您必须对下载的脚本进行外壳处理这一事实chmod +x
,我考虑了上述问题中的设计。
结束语
也许总而言之,也许我们应该保持简单:如果文件必须是可执行的,则使其可执行并让内核在用户调用时执行它。将任务委托给它所属的地方。