首页 > 解决方案 > 在 Django 后端使用 Clamav 设置文件上传流扫描

问题描述

使用 React/Django 应用程序。我有用户通过 React 前端上传的文件,这些文件最终在 Django/DRF 后端。我们在服务器上不断运行防病毒 (AV),但我们希望在将其写入磁盘之前添加流扫描。

如何设置它有点让我头疼。以下是我正在查看的一些来源。

您如何对上传到您的 java webapp 的文件进行病毒扫描?

尽管公认的最佳答案描述了它“......非常容易”设置,但我正在努力。

我显然需要cat testfile | clamscan -按照帖子和相应的文档:

您如何对上传到您的 java webapp 的文件进行病毒扫描?

因此,如果我的后端如下所示:

class SaveDocumentAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request, *args, **kwargs):

        # this is for handling the files we do want
        # it writes the files to disk and writes them to the database
        for f in request.FILES.getlist('file'):
            max_id = Uploads.objects.all().aggregate(Max('id'))
            if max_id['id__max'] == None:
                max_id = 1
            else:    
                max_id = max_id['id__max'] + 1
            data = {
                'user_id': request.user.id,
                'sur_id': kwargs.get('sur_id'),
                'co': User.objects.get(id=request.user.id).co,
                'date_uploaded': datetime.datetime.now(),
                'size': f.size
            }
            filename = str(data['co']) + '_' + \
                    str(data['sur_id']) + '_' + \
                    str(max_id) + '_' + \
                    f.name
            data['doc_path'] = filename
            self.save_file(f, filename)
            serializer = SaveDocumentSerializer(data=data)
            if serializer.is_valid(raise_exception=True):
                serializer.save()
        return Response(status=HTTP_200_OK)

    # Handling the document
    def save_file(self, file, filename):
        with open('fileupload/' + filename, 'wb+') as destination:
            for chunk in file.chunks():
                destination.write(chunk)

我想我需要在save_file方法中添加一些东西,比如:

for chunk in file.chunks():
    # run bash comman from python
    cat chunk | clamscan -
    if passes_clamscan:
        destination.write(chunk)
        return HttpResponse('It passed')
    else:
        return HttpResponse('Virus detected')

所以我的问题是:

1) 如何从 Python 运行 Bash?

2)如何接收扫描的结果响应,以便可以将其发送回用户,并且可以通过后端的响应来完成其他事情?(比如创建逻辑来向用户和管理员发送一封电子邮件,说明他们的文件有病毒)。

我一直在玩这个,但运气不佳。

在 Python 中运行 Bash 命令

此外,那里有 Github 存储库声称将 Clamav 与 Django 结合得很好,但它们要么多年未更新,要么现有文档非常糟糕。请参阅以下内容:

https://github.com/vstoykov/django-clamd

https://github.com/musashiXXX/django-clamav-upload

https://github.com/QueraTeam/django-clamav

标签: pythondjangoweb-applicationsantivirusantivirus-integration

解决方案


Ok, got this working with clamd. I modified my SaveDocumentAPIView to the following. This scans the files before they are written to disk and prevents them from being written if they infected. Still allows uninfected files through, so the user doesn't have to re-upload them.

class SaveDocumentAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request, *args, **kwargs):

        # create array for files if infected
        infected_files = []

        # setup unix socket to scan stream
        cd = clamd.ClamdUnixSocket()

        # this is for handling the files we do want
        # it writes the files to disk and writes them to the database
        for f in request.FILES.getlist('file'):
            # scan stream
            scan_results = cd.instream(f)

            if (scan_results['stream'][0] == 'OK'):    
                # start to create the file name
                max_id = Uploads.objects.all().aggregate(Max('id'))
                if max_id['id__max'] == None:
                    max_id = 1
                else:    
                    max_id = max_id['id__max'] + 1
                data = {
                    'user_id': request.user.id,
                    'sur_id': kwargs.get('sur_id'),
                    'co': User.objects.get(id=request.user.id).co,
                    'date_uploaded': datetime.datetime.now(),
                    'size': f.size
                }
                filename = str(data['co']) + '_' + \
                        str(data['sur_id']) + '_' + \
                        str(max_id) + '_' + \
                        f.name
                data['doc_path'] = filename
                self.save_file(f, filename)
                serializer = SaveDocumentSerializer(data=data)
                if serializer.is_valid(raise_exception=True):
                    serializer.save()

            elif (scan_results['stream'][0] == 'FOUND'):
                send_mail(
                    'Virus Found in Submitted File',
                    'The user %s %s with email %s has submitted the following file ' \
                    'flagged as containing a virus: \n\n %s' % \
                    (
                        user_obj.first_name, 
                        user_obj.last_name, 
                        user_obj.email, 
                        f.name
                    ),
                    'The Company <no-reply@company.com>',
                    ['admin@company.com']
                )
                infected_files.append(f.name)

        return Response({'filename': infected_files}, status=HTTP_200_OK)

    # Handling the document
    def save_file(self, file, filename):
        with open('fileupload/' + filename, 'wb+') as destination:
            for chunk in file.chunks():
                destination.write(chunk)

推荐阅读