Ограничение доступа к загрузкам частных файлов в Django

У меня есть несколько FileFields в мое приложение Джанго, которое может принадлежать разным пользователям. Я ищу хороший способ ограничить доступ к файлам для пользователя, который не является владельцем файла.

каков наилучший способ достичь этого? Есть идеи?

4 ответов


Unfortuanately решение @ Mikko не может фактически работать в производственной среде, так как django не предназначен для обслуживания файлов. В рабочей среде файлы должны обслуживаться вашим HTTP-сервером (e.g apache, nginx и т.д.) и не приложения/Джанго сервер (электронная.г на uwsgi, gunicorn, mod_wsgi и т. д.).

вот почему ограничение файла не очень легко: вам нужен способ для вашего HTTP-сервера спросить сервер приложений если это нормально, чтобы служить файл для конкретного пользователя, запрашивающего его. Как вы можете понять, это требует модификации как для вашего приложения и сервер http.

лучшим решением вышеуказанной проблемы является django-sendfile (https://github.com/johnsensible/django-sendfile), который использует механизм X-SendFile для реализации вышеизложенного. Я копирую из описания проекта:

это обертка вокруг конкретного веб-сервера способы отправки файлов веб-клиентам. Это полезно, когда Django необходимо проверить разрешения, связанные с файлами, но не хочет обслуживать фактические байты самого файла. т. е. как обслуживание больших файлов-это не то, для чего создан Django.

чтобы узнать больше о механизме senfile, пожалуйста, прочитайте этот ответ:Django-Понимание X-Sendfile

обновить 2018: обратите внимание, что django-sendfile, похоже, не поддерживается больше; вероятно, он все еще должен работать, однако, если вы хотите более современный пакет с аналогичной функциональностью взгляните наhttps://github.com/edoburu/django-private-storage как комментатор @surfer190 предлагает. Особенно убедитесь, что вы внедрили раздел "оптимизация больших передач файлов"; на самом деле это нужно для всех передач не только для больших файлов.


Если вам нужна только умеренная безопасность, мой подход будет следующим:

1) Когда пользователь загружает файл, создайте для него трудный для угадывания путь. Например, вы можете создать папку со случайным именем для каждого загружаемого файла в /статический папку. Вы можете сделать это довольно просто, используя этот пример кода:

file_path = "/static/" + os.urandom(32).encode('hex') + "/" + file_name

таким образом, будет очень трудно догадаться, где хранятся файлы других пользователей.

2) в базе данных ссылок владельца в папку. Примером схемы может быть:

uploads(id, user_id, file_path)

3) используйте свойство для ваших файловых полей в модели, чтобы ограничить доступ к файлу, таким образом:

class YourModel(models.Model)
    _secret_file = models.FileField()

    def get_secret_file(self):
        # check in db if the user owns the file
        if True:
            return self._secret_file
        elif:
            return None # or something meaningful depanding on your app

    secret_file = property(get_secret_file)

как правило, вы не маршрутизируете личные файлы через обычный статический файл, обслуживающий непосредственно через Apache, Nginx или любой веб-сервер, который вы используете. Вместо этого напишите пользовательское представление Django, которое обрабатывает проверку разрешений, а затем возвращает файл в виде потоковой загрузки.

  • убедитесь, что файлы находятся в специальной папке частной папки и не подвергаются через Django MEDIA_URL или STATIC_URL

  • напишите представление, которое Уилл!--6-->

    • убедитесь, что пользователь имеет доступ к файлу в вашей логике просмотра

    • откройте файл с помощью Python open()

    • возвращает HTTP-ответ, который получает дескриптор файла в качестве параметра http.HttpResponse(_file, content_type="text/plain")

например, см. download() здесь.


это лучше всего обрабатывается сервером, например nginx модуль безопасной связи (nginx должен быть скомпилирован с --with-http_secure_link_module)

пример из документации:

location /some-url/ {
    secure_link $arg_md5,$arg_expires;
    secure_link_md5 "$secure_link_expires$uri$remote_addr some-secret";

    if ($secure_link = "") {
        return 403;
    }

    if ($secure_link = "0") {
        return 410;
    }

    if ($secure_link = "1") {
        // authorised...
    }
}

файл будет доступен, как:

/some-url/some-file?md5=_e4Nc3iduzkWRm01TBBNYw&expires=2147483647

(это будет ограничено по времени и связано с пользователем на этом IP-адресе).

создание маркера для передачи пользователю будет использовать что-то вроде:

echo -n 'timestamp/some-url/some-file127.0.0.1 some-secret' | \
openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =