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

Django图片上传失败:SuspiciousFileOperation路径遍历错误求助

Django图片上传触发SuspiciousFileOperation错误,提示路径遍历

报错信息

django.core.exceptions.SuspiciousFileOperation: 
Detected path traversal attempt in '/media/uploads/87176296b7b2425e81e266eaed65019b.png'
Bad Request: /api/upload_file/
Bad Request: /api/upload_file/
[03/Aug/2023 17:47:52] "POST /api/upload_file/ HTTP/1.1" 400 17430

相关代码

/api/upload_file/接口代码

def upload_file(request):
    request_file = request.FILES['file'] if 'file' in request.FILES else None
    if request_file is None:
        data={
            "error": "No file",
        }
        return Response(status=status.HTTP_400_BAD_REQUEST, data=data)
    if request_file.size > 20*1024*1024:
        data={
            "error": "Image file too large (>20mb)",
        }
        return Response(status=status.HTTP_400_BAD_REQUEST, data=data)
    allowed_file_types = ['jpeg', 'jpg', 'png']
    file_type = imghdr.what(request_file)
    if file_type not in allowed_file_types:
        data = {
            "error": "Invalid file type",
        }
        return Response(status=status.HTTP_400_BAD_REQUEST, data=data)
    upload = Upload(image=request_file)
    upload.save()
    data = {
        "upload_id": upload.id,
    }
    return Response(status=status.HTTP_200_OK, data=data)

Upload模型代码

class Upload(models.Model):
    image = models.ImageField(
        verbose_name="Image",
        upload_to=path_and_rename_upload,
    )
    patient = models.ForeignKey(
        "users.Patient",
        verbose_name="Patient",
        related_name="uploads",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
    )
    upload_date = models.DateTimeField(
        verbose_name="Upload Date",
        auto_now_add=True,
    )

    def __str__(self):
        return self.image.name

@deconstructible
class PathAndRename(object):

    def __init__(self, sub_path):
        self.path = sub_path

    def __call__(self, instance, filename) -> str:
        ext = filename.split(".")[-1]
        filename = f"{uuid4().hex}.{ext}".lower()
        return os.path.join(self.path, filename)


path_and_rename_upload = PathAndRename("/media/uploads")

settings.py配置

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]
if not DEBUG:
    STATIC_ROOT = os.path.join(BASE_DIR, 'static')


MEDIA_URL = '/media/'
if not DEBUG:
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

问题原因及解决办法

  1. 核心问题
    Django的ImageField的upload_to参数要求传入相对路径,你当前传入的"/media/uploads"是绝对路径格式,被Django判定为路径遍历攻击,触发SuspiciousFileOperation错误。

  2. 修复步骤

  • 修正upload_to的路径参数:去掉路径开头的斜杠,改为相对路径

    # 原代码
    path_and_rename_upload = PathAndRename("/media/uploads")
    # 修改后
    path_and_rename_upload = PathAndRename("uploads")
    

    这样Django会自动将路径拼接在MEDIA_ROOT之后,最终保存路径为BASE_DIR/media/uploads/xxx.png。

  • 完善MEDIA_ROOT配置:确保DEBUG模式下也有正确的MEDIA_ROOT设置(当前仅在非DEBUG时配置,会导致DEBUG模式下路径异常)

    MEDIA_URL = '/media/'
    # 统一设置,覆盖原条件判断
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    
  • 保留路径生成逻辑:你的PathAndRename类的路径拼接逻辑无需修改,只要传入的sub_path是相对路径,os.path.join会自动生成符合要求的相对路径。

  1. 验证结果
    修复后,上传的文件会被保存到BASE_DIR/media/uploads/目录,数据库中存储的路径为uploads/xxx.png,通过MEDIA_URL可以正常访问该文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 01:47:07