限制路径访问以防止路径遍历

我们通常使用路径来引用磁盘上的文件或其他资源。路径遍历攻击是指攻击者提供的输入与我们的路径一起使用,从而访问文件系统上我们未预期的文件。该输入通常试图突破应用程序的工作目录并访问文件系统上的其他文件。可以通过限制文件系统访问范围并使用受限的文件权限配置文件来减少攻击面,从而缓解此类攻击。

错误

在 Web 应用程序中,路径遍历的典型远程向量可能涉及在文件系统上提供或存储文件。考虑以下示例

import os
from flask import Flask, redirect, request, send_file

app = Flask(__name__)

@app.route('/')
def cat_picture():
    image_name = request.args.get('image_name')
    if not image_name:
        return 404
    return send_file(os.path.join(os.getcwd(), image_name))


if __name__ == '__main__':
    app.run(debug=True)

由于攻击者控制直接用于构造路径的输入,因此他们能够访问系统上的任何文件。例如,考虑攻击者发起如下请求会发生什么

curl http://example.com/?image_name=../../../../../../../../etc/passwd

路径遍历漏洞也可能发生在解压缩文件压缩包时。OpenStack 中发生过这种情况的一个例子是 OSSA-2011-001。在这种情况下,来自不受信任来源的 tar 文件可以被解压缩以覆盖主机操作系统上的文件。

import tarfile

def untar_image(path, filename):
    tar_file = tarfile.open(filename, 'r|gz')
    tar_file.extract_all(path)
    image_file = tar_file.get_names()[0]
    tar_file.close()
    return os.path.join(path, image_file)

正确

以下示例演示了如何使用 Python 代码来限制对特定目录中文件的访问。这可以用作阻止路径遍历的一种机制。

import os
import sys

def is_safe_path(basedir, path, follow_symlinks=True):
    # resolves symbolic links
    if follow_symlinks:
        matchpath = os.path.realpath(path)
    else:
        matchpath = os.path.abspath(path)
    return basedir == os.path.commonpath((basedir, matchpath))


def main(args):
    for arg in args:
        if is_safe_path(os.getcwd(), arg):
            print("safe: {}".format(arg))
        else:
            print("unsafe: {}".format(arg))

if __name__ == "__main__":
    main(sys.argv[1:])

另一种限制文件系统访问的方法是维护唯一标识符与操作系统上存在的文件路径之间的间接映射。这可以防止用户提供恶意输入来访问未经授权的文件。

localfiles = {
  "01" : "/var/www/img/001.png",
  "02" : "/var/www/img/002.png",
  "03" : "/var/www/img/003.png",
}

# Will raise an error if an invalid key is used.
def get_file(file_id):
  return open(localfiles[file_id])

后果

未验证文件路径允许攻击者读取或写入应用程序可以访问的任何文件。这可能导致信息泄露,并可用于转向其他更严重的攻击,例如远程代码执行。

参考