注意:从 python 2.7.4 开始,这对于 ZIP 档案来说不是问题。答案底部的详细信息。这个答案侧重于 tar 档案。
要找出路径真正指向的位置,请使用os.path.abspath()
(但请注意关于将符号链接作为路径组件的警告)。如果您规范化 zipfile 中的路径abspath
并且它不包含当前目录作为前缀,则它指向它之外。
但是您还需要检查从存档中提取的任何符号链接的值(tarfile 和 unix zipfile 都可以存储符号链接)。如果您担心众所周知的“恶意用户”会故意绕过您的安全性,而不是简单地将自身安装在系统库中的应用程序,这一点很重要。
这就是前面提到的警告:abspath
如果您的沙箱已经包含指向目录的符号链接,则会被误导。即使是指向沙箱内的符号链接也可能很危险:符号链接sandbox/subdir/foo -> ..
指向,因此应该禁止sandbox
该路径。sandbox/subdir/foo/../.bashrc
最简单的方法是等到之前的文件被提取并使用 os.path.realpath()
. 幸运的是extractall()
接受了一个生成器,所以这很容易做到。
由于您要求提供代码,因此这里有一些解释算法的内容。它不仅禁止将文件提取到沙箱外的位置(这是所要求的),而且还禁止在沙箱内创建指向沙箱外位置的链接。我很想知道是否有人可以将任何杂散文件或链接偷偷溜过去。
import tarfile
from os.path import abspath, realpath, dirname, join as joinpath
from sys import stderr
resolved = lambda x: realpath(abspath(x))
def badpath(path, base):
# joinpath will ignore base if path is absolute
return not resolved(joinpath(base,path)).startswith(base)
def badlink(info, base):
# Links are interpreted relative to the directory containing the link
tip = resolved(joinpath(base, dirname(info.name)))
return badpath(info.linkname, base=tip)
def safemembers(members):
base = resolved(".")
for finfo in members:
if badpath(finfo.name, base):
print >>stderr, finfo.name, "is blocked (illegal path)"
elif finfo.issym() and badlink(finfo,base):
print >>stderr, finfo.name, "is blocked: Hard link to", finfo.linkname
elif finfo.islnk() and badlink(finfo,base):
print >>stderr, finfo.name, "is blocked: Symlink to", finfo.linkname
else:
yield finfo
ar = tarfile.open("testtar.tar")
ar.extractall(path="./sandbox", members=safemembers(ar))
ar.close()
编辑:从 python 2.7.4 开始,这对 ZIP 档案来说不是问题:该方法zipfile.extract()
禁止在沙箱外创建文件:
注意:如果成员文件名是绝对路径,驱动器/UNC 共享点和前导(反)斜杠将被剥离,例如:在 Unix 上///foo/bar
变为在Windows 上。并且成员文件名中的所有组件都将被删除,例如:变成. 在 Windows 上,非法字符 ( , , , , , , 和) [被] 替换为下划线 (_)。foo/bar
C:\foo\bar
foo\bar
".."
../../foo../../ba..r
foo../ba..r
:
<
>
|
"
?
*
该tarfile
课程尚未经过类似的消毒,因此上述答案仍然适用。