You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python/Django中跨平台规范化相对路径的安全方案

跨平台规范化Django文件上传相对路径的问题及解决方案

问题场景与错误代码

在Django中实现跨平台的带时间戳文件上传路径时,原代码触发SuspiciousFileOperation异常,代码如下:

def get_filepath(instance, filename) -> str:
    """
    Returns a timestamped directory path to deposit files into.

    i.e. "evidence/2023/02/23/96672aa5-94d9-4289-8531-d7a8dc8f060d/data.jpg"

    """
    dt = timezone.now()
    YYYY = dt.year
    MM = str(dt.month).zfill(2)
    DD = str(dt.day).zfill(2)
    UID = str(uuid.uuid4())

    # settings.MEDIA_URL = 'media/', I just checked
    path = (str(x) for x in (
        settings.MEDIA_URL, 'evidence', YYYY, MM, DD, UID, filename
    ))
    path = os.path.join(*path) # dubious
    return path

错误信息

django.core.exceptions.SuspiciousFileOperation: Detected path traversal attempt in '/media/evidence\2023\02\23\1cfa40b9-b522-4c70-b59b-fd270d722ab2\templeos.txt'

尝试过的无效方法

  • 使用pathlib.PurePath:Django的FileField无法直接识别Path对象,会误判为绝对路径并添加系统前缀
  • 使用os.path.normpath:因路径包含绝对前缀,仍触发"路径超出基础组件"的异常
  • 手动清理路径组件:虽生效但实现粗糙,希望用正规库处理

跨平台规范化相对路径的标准做法

1. 核心修正:区分URL路径与文件系统路径

Django的upload_to函数返回的是相对于MEDIA_ROOT的文件系统路径,不需要包含MEDIA_URL(这是前端访问用的URL前缀)。原代码的核心错误就是将MEDIA_URL(带开头斜杠的绝对URL路径)混入文件系统路径拼接,导致跨平台下被误判为路径遍历。

2. 用pathlib实现跨平台路径拼接(推荐)

pathlib是Python3.4+的标准库,自动适配不同平台的路径分隔符,转为字符串后可直接被Django识别:

from pathlib import Path
import uuid
from django.utils import timezone
from django.utils.text import get_valid_filename

def get_filepath(instance, filename) -> str:
    """
    Returns a timestamped directory path to deposit files into.
    i.e. "evidence/2023/02/23/96672aa5-94d9-4289-8531-d7a8dc8f060d/data.jpg"
    """
    # 清理文件名,防止路径遍历字符(如../)
    filename = get_valid_filename(filename)
    
    dt = timezone.now()
    path_components = [
        "evidence",
        str(dt.year),
        f"{dt.month:02d}",
        f"{dt.day:02d}",
        str(uuid.uuid4()),
        filename
    ]
    # 拼接后转为字符串,自动适配平台分隔符
    return str(Path(*path_components))

3. 用os.path.join实现跨平台拼接

如果偏好传统方式,确保所有路径组件都是相对路径(无开头斜杠),os.path.join会自动处理跨平台分隔符:

import os
import uuid
from django.utils import timezone
from django.utils.text import get_valid_filename

def get_filepath(instance, filename) -> str:
    filename = get_valid_filename(filename)
    dt = timezone.now()
    return os.path.join(
        "evidence",
        str(dt.year),
        f"{dt.month:02d}",
        f"{dt.day:02d}",
        str(uuid.uuid4()),
        filename
    )

4. 额外安全措施:清理用户上传的文件名

使用Django内置的get_valid_filename函数,可自动过滤文件名中的路径遍历字符(如../)、特殊符号等,进一步避免安全风险。


内容的提问来源于stack exchange,提问作者Ivan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 23:27:45