Django Rest Framework 和 React 前端:如何防止未经授权的用户在获得图像 URL 的情况下查看私人图像?

Posted

技术标签:

【中文标题】Django Rest Framework 和 React 前端:如何防止未经授权的用户在获得图像 URL 的情况下查看私人图像?【英文标题】:Django Rest Framework and React Front End: How to prevent unauthorized users from viewing private images if they get a hold of the image URL? 【发布时间】:2020-08-14 09:24:03 【问题描述】:

我的理解是,在一个普通的 React 网站上,所有媒体都将来自与前端文件(html、CSS 和 javascript)相同的服务器。

REST API 后端将仅提供指向这些图像存储位置的链接,并由前端提供和显示。

问题是,如果这些图像是私有的,并且应该只有一个用户可以看到它们。如果有人获得图片 URL 并打开它,他们将能够看到图片。

现代网站如何解决这个问题? Django 和 React 之间的工作流程是什么来保证用户图像的安全和私密?

【问题讨论】:

现代网站通过临时链接来解决这个问题。例如,Facebook 上的照片链接有效期为几周/几天,具体取决于链接。 另一个更私密的可能解决方案是在用户和静态文件主机之间设置身份验证网关。 F.e.您在 s3 和您自己的身份验证服务器上有文件存储。来自您的服务器的用户请求图像,您的服务器检查用户令牌或其他内容,并将下载流从 s3 管道传输到用户。 另一种是使用nginx的X-Accel-Redirect header。对用户隐藏真实图像并控制下载也将非常有用。首先,用户使用令牌请求 nginx,nginx 重定向到您的身份验证服务器,您的身份验证服务器写入此重定向标头并将响应返回给 nginx(fe 它包括路径到您的私有存储中),nginx 会捕获此标头作为响应,使用它们从您的私有存储中下载图像并将其从对用户的最终答案中删除。所以如果他只有链接,另一个用户将无法访问图像,他也需要令牌 我刚刚在 ycombinator 上看到了一条评论!看看吧news.ycombinator.com/item?id=23057465 【参考方案1】:
Step 1: All requests for media should go throught a specific view

Edit file myproject/urls.py and add:

from myproject.views import media_access

urlpatterns = [
    ...,
    url(r'^media/(?P<path>.*)', media_access, name='media'),
]


Step 2: Add the view and check access

Add the following view in myproject/views.py:

from django.http import HttpResponse
from django.http import HttpResponseForbidden

def media_access(request, path):
    """
    When trying to access :
    myproject.com/media/uploads/passport.png

    If access is authorized, the request will be redirected to
    myproject.com/protected/media/uploads/passport.png

    This special URL will be handle by nginx we the help of X-Accel
    """

    access_granted = False

    user = request.user
    if user.is_authenticated():
        if user.is_staff:
            # If admin, everything is granted
            access_granted = True
        else:
            # For simple user, only their documents can be accessed
            user_documents = [
                user.identity_document,
                # add here more allowed documents
            ]

            for doc in user_documents:
                if path == doc.name:
                    access_granted = True

    if access_granted:
        response = HttpResponse()
        # Content-type will be detected by nginx
        del response['Content-Type']
        response['X-Accel-Redirect'] = '/protected/media/' + path
        return response
    else:
        return HttpResponseForbidden('Not authorized to access this media.')


Here the important part is about the variable user_documents. It should contain reference to all files the user has access. In my case I extended the User model to add a field identity_document that why I can access it here.

In this media_acces view, you can implement any authorization logic.


Step 3: Configure nginx

Here is a full example of nginx configuration. The important part is about location /protected/. We suppose that Django is available with Gunicorn on port 8080 for example.

upstream myprojectapp 
  server localhost:8080;


server 
    listen 80;

    server_name myproject.com;

    server_name_in_redirect on;
    error_log /var/log/nginx/myproject-error.log crit;
    access_log  /var/log/nginx/myproject-access.log custom_combined;

    root /path/to/my/django/project/static;

    location ^~ /static/ 
        alias /path/to/my/django/project/static/;
    

    location /protected/ 
        internal;
        alias /path/to/my/django/project/;
    

    location / 
        include proxy_params;
        proxy_pass http://myprojectapp;
        proxy_buffering off;
    



You can notice there is no location ^~ /media/ that's because this URL is handle by django.

【讨论】:

【参考方案2】:

这更像是对其他人关于X-Accel 的回答的补充。 X-Accel 解决了问题的前半部分,即为链接设置了看门人。

问题的另一半是有时您无法轻松地对用户进行身份验证,例如在典型的 React/DRF 设置中,您将使用 JWT,并且无法控制哪些标头浏览器将发送到您的服务器。您将有以下选择

    您需要选择使用基于 cookie 的身份验证

    在图像链接中实现自定义身份验证机制,类似于 AWS S3 预签名 URL 的工作方式。一个简单的方法是根据 Nginx secure_link 模块生成图像的 URL。 参考:https://www.nginx.com/blog/securing-urls-secure-link-module-nginx-plus/

我选择方法 #2,通过自定义字段/序列化程序类(即SecuredFilePathSerializer)并使用它而不是FilePathField

【讨论】:

【参考方案3】:

实际上,在 REST API 端点提供图像 URL 之前,可以使用 Django REST frameworks 授权框架正确授权端点的请求和响应。

此外,您可以为您想要的最终目标编写自己的自定义 Django 中间件 [HTTP 挂钩]。

中间件可以根据您的需要自定义 HTTP 请求-响应行为。

【讨论】:

以上是关于Django Rest Framework 和 React 前端:如何防止未经授权的用户在获得图像 URL 的情况下查看私人图像?的主要内容,如果未能解决你的问题,请参考以下文章

Django rest-framework框架-组件之路由

python-django rest framework框架之路由

Django 1.8 和 Rest Framework 3.7 的“导入错误:没有名为 urls 的模块”

在 django-rest-framework 中插入 django-allauth 作为端点

如何在 Django 的单个 API 中获取访问令牌和刷新令牌(rest_framework_jwt)

根据请求类型更改 Django REST Framework ModelSerializer 中的字段?