Ограничение доступа к загрузкам частных файлов в 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 =