FlawOpen

flawopen.com/Обход пути/Python os.path.join()

Защищает ли Python os.path.join() от Path Traversal?

CWE-22: Path TraversalСправочная страница
Краткий ответ

Нет. Представьте базовый каталог комнатой: .. ведет через родительские комнаты наружу; абсолютный путь — новый адрес, из-за которого os.path.join() отбрасывает исходную комнату. Разрешите итоговый путь и проверьте, что он остается в разрешенном каталоге, прежде чем открыть файл.

VULNERABLE
import os

base_dir = "/srv/uploads"
user_filename = "../../outside/demo.txt"
path = os.path.join(base_dir, user_filename)
# Relative traversal can leave the base.

path = os.path.join(base_dir, "/outside/demo.txt")
# An absolute segment discards base_dir.
SECURE — resolve and check containment
import os

def resolve_under_base(base_dir, user_filename):
    if not isinstance(user_filename, str) or not user_filename:
        raise ValueError("Invalid filename")

    base = os.path.realpath(base_dir)
    candidate = os.path.realpath(
        os.path.join(base, user_filename)
    )

    try:
        inside = os.path.commonpath((base, candidate)) == base
    except ValueError:  # e.g. different Windows drives
        inside = False

    if not inside or candidate == base:
        raise ValueError("Invalid filename")
    return candidate

Два свойства объединения путей

Компонент .. может вывести относительный путь из нужного каталога. По документации Python абсолютный компонент заставляет os.path.join() отбросить предыдущие; правила дисков и корней Windows отличаются от POSIX. pathlib.Path(base) / user_input ведет себя так же. Объединение и нормализация не проверяют права.

Почему commonpath() безопаснее строковых префиксов

Перед сравнением разрешите доверенную базу и кандидат. os.path.commonpath() сравнивает компоненты, а не префиксы символов, и вызывает ValueError для несовместимых корней или дисков; отклоняйте такие случаи. realpath() разрешает ссылки на момент проверки, но не мешает изменить их до открытия. Если дерево доступно для изменения, используйте подходящие дескрипторы и no-follow.

Частые вопросы

В pathlib такое же поведение?

Да. Оператор / у Path отбрасывает предыдущие компоненты, если следующий путь абсолютный. Разрешите результат и проверьте разрешенную базу.

realpath() устраняет гонки?

Нет. Он разрешает ссылки при проверке; другой процесс может изменить их до открытия. Ограничьте запись или используйте поддерживаемые операции относительно дескриптора с no-follow.

Источники

См. также Обход каталогов (CWE-22): как это работает и как предотвратить на любом языке — flawopen.com Что такое уязвимость Zip Slip? Node.js path.join() не защищает от Path Traversal PHP include() и пути, заданные пользователем