python - 使用 Python 安全地提取 zip 或 tar

标签 python zip tar tarfile python-zipfile

我正在尝试将用户提交的 zip 和 tar 文件提取到一个目录中。 zipfile 的文档 extractall方法(与 tarfile 的 extractall 类似)指出路径可能是绝对路径或包含超出目标路径的 .. 路径。相反,我可以自己使用 extract,如下所示:

some_path = '/destination/path'
some_zip = '/some/file.zip'
zipf = zipfile.ZipFile(some_zip, mode='r')
for subfile in zipf.namelist():
    zipf.extract(subfile, some_path)

这样安全吗?在这种情况下,存档中的文件是否有可能在 some_path 之外结束?如果是这样,我怎样才能确保文件永远不会在目标目录之外结束?

最佳答案

注意:从 python 2.7.4 开始,这对于 ZIP 存档不存在问题。答案底部的详细信息。此答案侧重于 tar 文件。

要确定路径真正指向的位置,请使用 os.path.abspath() (但请注意有关将符号链接(symbolic link)作为路径组件的警告)。如果您使用 abspath 规范化 zip 文件中的路径它确实包含当前目录作为前缀,它指向它之外。

但您还需要检查从存档中提取的任何符号链接(symbolic link)的 (tarfile 和 unix zipfile 都可以存储符号链接(symbolic link))。如果您担心众所周知的“恶意用户”会故意绕过您的安全性,而不是简单地将自身安装在系统库中的应用程序,这一点很重要。

这就是前面提到的警告:abspath如果您的沙箱已经包含指向目录的符号链接(symbolic link),则会被误导。即使是指向沙箱内的符号链接(symbolic link)也可能很危险:符号链接(symbolic link) 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() 禁止在沙箱外创建文件:

Note: If a member filename is an absolute path, a drive/UNC sharepoint and leading (back)slashes will be stripped, e.g.: ///foo/bar becomes foo/bar on Unix, and C:\foo\bar becomes foo\bar on Windows. And all ".." components in a member filename will be removed, e.g.: ../../foo../../ba..r becomes foo../ba..r. On Windows, illegal characters (:, <, >, |, ", ?, and *) [are] replaced by underscore (_).

tarfile类还没有经过类似的清理,所以上面的答案仍然适用。

关于python - 使用 Python 安全地提取 zip 或 tar,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10060069/

相关文章:

linux - 无法在终端中解压缩 .gz 文件 - file.gz : not in gzip format

python - django 平面页面和重定向应用程序 - 区分大小写

python - clang.cindex.Libclang错误 "Undefined symbol clang_CXXRecord_isAbstract"

python - 什么可以使Python的日期列处理更快

powershell - 通过 Powershell 从 zip 中提取某个文件似乎不在子文件夹中查找

ruby-on-rails - 从 ruby​​ on rails 生成受密码保护的 zip 文件

android - xamarin ionic zip 解压缩导致错误

bash - 如何将进度条添加到 somearchive.tar.xz 提取

linux - Golang 压缩目录

python - 强制对函数调用进行默认重新评估