Currently, we have a configuration in Django to serve media files (these are the files uploaded by users in the system).
We have a URL for this where we capture all the files in the media directory and redirect them to the serve_media view.
re_path(r"^media/(?P<path>.*)$", cms.views.serve_media),
This is the serve_media view:
def serve_media(request, path=""):
if request.user.is_authenticated:
user = request.user
else:
return redirect("/%s?next=%s" % (settings.LOGIN_URL, "/media/" + path))
usuario = Usuario.objects.get(usuario=user)
# Search for the file by its path in the media folder and check if the user
# has permission to access it
allowed_to_serve = False
try:
arquivo = Arquivo.objects.get(arquivo__contains=path)
# Public image of a project on the website
for projeto in Projeto.objects.exclude(id_imagem_publica=None):
if projeto.arquivo_in_id_imagem_publica(arquivo.id) is True:
allowed_to_serve = True
break
if user == arquivo.criado_por or usuario.permissao == Usuario.SUPER:
allowed_to_serve = True
# Entidade file
if arquivo.entidade is not None and allowed_to_serve is False:
if (
usuario.permissao == Usuario.ENTIDADE
and user == arquivo.entidade.usuario.usuario
):
allowed_to_serve = True
elif (
usuario.permissao == Usuario.AGENCIA
and arquivo.entidade.projeto_set.all()
.filter(cidade_atuacao=usuario.cidade_atuacao)
.count()
> 0
):
allowed_to_serve = True
# Projeto file
if arquivo.projeto is not None and allowed_to_serve is False:
if (
usuario.permissao == Usuario.ENTIDADE
and user == arquivo.projeto.entidade.usuario.usuario
):
allowed_to_serve = True
elif (
usuario.permissao == Usuario.AGENCIA
and arquivo.projeto.cidade_atuacao == usuario.cidade_atuacao
):
allowed_to_serve = True
elif (
usuario.permissao == Usuario.EMBAIXADOR
and arquivo.projeto.cidade_atuacao == usuario.cidade_atuacao
):
allowed_to_serve = True
# Prestacao contas file
if arquivo.prestacao_contas is not None and allowed_to_serve is False:
if (
usuario.permissao == Usuario.ENTIDADE
and user == arquivo.prestacao_contas.projeto.entidade.usuario.usuario
):
allowed_to_serve = True
elif (
usuario.permissao == Usuario.AGENCIA
and arquivo.prestacao_contas.projeto.cidade_atuacao == usuario.cidade_atuacao
):
allowed_to_serve = True
elif (
usuario.permissao == Usuario.EMBAIXADOR
and arquivo.prestacao_contas.projeto.cidade_atuacao == usuario.cidade_atuacao
):
allowed_to_serve = True
# Embaixador opinion file
if arquivo.parecer is not None and allowed_to_serve is False:
if (
usuario.permissao == Usuario.AGENCIA
and arquivo.parecer.projeto.cidade_atuacao == usuario.cidade_atuacao
):
allowed_to_serve = True
elif (
usuario.permissao == Usuario.EMBAIXADOR
and arquivo.parecer.projeto.cidade_atuacao == usuario.cidade_atuacao
):
allowed_to_serve = True
except Exception:
pass
if allowed_to_serve is False:
return HttpResponseForbidden("")
return serve(request, path, settings.MEDIA_ROOT)
On localhost, this control works as intended, restricting access to certain files only for logged-in users and allowing other files to be accessible, such as public project photos.
Our problem is that on the server, the files are being served by nginx
, and it ends up “overriding” the Django configuration, serving the files directly without validation, making internal system files available for public access.
This is our current configuration in nginx. Note that we have already blocked some extensions and pointed to the project’s media directory. However, the final step of forwarding the request to the serve_media
view is missing, and it is currently being ignored.
location /media {
location ~* .(html|js|css|php|bak|swp|tmp|zip|tar.gz)$ {
return 403;
}
alias /home/django/fundo/fundo/media;
add_header 'Access-Control-Allow-Origin' 'fundo.url.com.br';
add_header 'X-Content-Type-Options' 'nosniff';
add_header 'Cache-Control' 'max-age=600, s-maxage=600, max-stale=600, must-revalidate, proxy-revalidate';
}