如何只允许在Python3中打开当前目录中的文件?

时间:2017-01-13 11:07:21

标签: python security filenames

我正在用Python编写一个简单的文件服务器。文件名由客户端提供,应视为不可信。如何验证它是否与当前目录(在其中或其任何子目录中)内的文件相对应?会是这样的:

pwd=os.getcwd()
if os.path.commonpath((pwd,os.path.abspath(filename))) == pwd:
    open(filename,'rb')

足够?

1 个答案:

答案 0 :(得分:3)

使用os.path.realpath将文件名转换为规范路径,获取目录部分,并查看当前目录(以规范形式)是否为前缀:

import os, os.path

def in_cwd(fname):
    path = os.path.dirname(os.path.realpath(fname))
    return path.startswith(os.getcwd())

通过将fname转换为规范路径,我们处理包含../的符号链接和路径。

<强>更新

不幸的是,上面的代码有点问题。例如,

'/a/b/cd'.startswith('/a/b/c')

返回True,但我们肯定想要这种行为!幸运的是,有一个简单的解决方法:我们只需要在执行前缀测试之前将os.sep附加到路径。新版本还通过os.path.normcase处理任何操作系统路径名不区分大小写的问题。

import os, os.path

def clean_dirname(dname):
    dname = os.path.normcase(dname)
    return os.path.join(dname, '')

def in_cwd(fname):
    cwd = clean_dirname(os.getcwd())
    path = os.path.dirname(os.path.realpath(fname))
    path = clean_dirname(path)
    return path.startswith(cwd)

感谢DSM指出前一段代码中的缺陷。

这是一个效率更高的版本。它使用os.path.commonpath,它比附加os.sep和执行字符串前缀测试更强大。

def in_cwd(fname):
    cwd = os.path.normcase(os.getcwd())
    path = os.path.normcase(os.path.dirname(os.path.realpath(fname)))
    return os.path.commonpath((path, cwd)) == cwd