如何仅使用相对路径进行连接?

1 投票
1 回答
725 浏览
提问于 2025-04-18 11:43

为了创建一个简单的网页服务器脚本,我写了一个函数,用来把网址转化为文件系统中的路径。

def resolve(url):
    url = url.lstrip('/')
    path = os.path.abspath(os.path.join(os.path.dirname(__file__), url))
    return path 

下面是一些例子,展示了当__file__变量是C:\projects\resolve.py时的输出结果。

/index.html    => C:\projects\index.html
/\index.html   => C:\index.html
/C:\index.html => C:\index.html

第一个例子是正常的。网址被转化为脚本所在目录中的一个文件。但是,第二个和第三个例子让我感到意外。因为附加的路径被当作绝对路径来处理,这样就完全忽略了脚本文件所在的目录。

这就带来了安全隐患,因为这样可以访问文件系统中的所有文件,而不仅仅是脚本子目录中的文件。为什么Python的os.path.join会允许和绝对路径连接?我该如何防止这种情况发生呢?

1 个回答

3

os.path.join() 这个函数不适合处理不安全的输入,没错。它的设计就是这样的:当你给它一个绝对路径时,它会忽略之前的参数。这种设计让你可以在配置文件中同时支持绝对路径和相对路径,而不需要去检查用户输入的路径。只要用 os.path.join(standard_location, config_path),它会自动帮你处理好。

你可以看看 Flask的 safe_join(),这个函数可以用来处理不可信的文件名:

import posixpath
import os.path

_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
                    if sep not in (None, '/'))

def safe_join(directory, filename):
    # docstring omitted for brevity
    filename = posixpath.normpath(filename)
    for sep in _os_alt_seps:
        if sep in filename:
            raise NotFound()
    if os.path.isabs(filename) or \
       filename == '..' or \
       filename.startswith('../'):
        raise NotFound()
    return os.path.join(directory, filename)

这个函数使用 posixpath(这是一个与平台无关的 os.path 模块的实现)来先规范化URL路径;这样可以去掉任何嵌入的 .././ 路径部分,确保它是一个完全规范的相对路径或绝对路径。

然后,它会排除任何其他的分隔符,比如说你不能用 /\index.html。最后,绝对文件名和相对文件名也是明确禁止的。

撰写回答