Расшифровка полей изображений для ответов REST API Django

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

def encrypt_validated_data(obj, image_name):
  for data_key in obj.validated_data:
    if type(obj.validated_data[data_key]) is InMemoryUploadedFile:
      file_extension = obj.validated_data[data_key].image.format.lower()
      file_extension = "." + file_extension
      file_name = image_name + file_extension
      encrypted_file = obj.cipher.encrypt(
        obj.validated_data[data_key].read()
      ).decode("utf-8")
      
      obj.validated_data[data_key] = ContentFile(
        encrypted_file,
        name = file_name
      )
    else:
      obj.validated_data[data_key] = obj.cipher.encrypt(
        obj.validated_data[data_key].encode("utf-8")
      ).decode("utf-8")
  
  return obj

Этот код вызывается всякий раз, когда объект собирается быть сохраненным в базе данных. Он шифрует все поля и работает с изображением. Теперь мне нужно расшифровать эти данные для моего ответа. Моей первой мыслью было манипулировать to_representation в сериализаторе таким образом, чтобы я мог расшифровать каждую часть данных. Это отлично работает для текстовых полей, но я не придумал, как это сделать с полями изображений! Это код на данный момент...

class ProfileSerializer(serializers.ModelSerializer):
  cipher = Fernet(settings.ENCRYPTED_FIELDS_KEY)
  profile_picture = serializers.ImageField(required=False)
  
  class Meta:
    model = YearRepresentative
    fields = [
      "user_id",
      "name",
      "profile_picture",
      "pronouns"
    ]
  
  def to_representation(self, instance):
    encrypted_data = super().to_representation(instance)
    for key in encrypted_data:
      # We keep user ids encrypted
      if key == "user_id" or encrypted_data[key] is None:
        pass
      elif key == "profile_picture":
        encrypted_picture = instance.profile_picture
        
        encrypted_file_content = encrypted_picture.read()
        
        decrypted_file_content = self.cipher.decrypt(encrypted_file_content)
        
        decrypted_picture = ContentFile(
          decrypted_file_content,
          name = encrypted_picture.name
        )
        encrypted_data[key] = decrypted_picture 
      else:
        encrypted_field = encrypted_data[key]
        encrypted_data[key] = self.cipher.decrypt(encrypted_field).decode("utf-8")
        
    return encrypted_data

Когда я вызываю encrypted_data[key] = decrypted_picture, я получаю сообщение об ошибке...

'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte

Unicode error hint
The string that could not be encoded/decoded was: �PNG

Это имеет смысл, но я не уверен, как именно это решить. Чего я хочу в конечном итоге добиться с помощью этой реализации, так это того, что расшифрованное изображение будет доступно только при вызовах API, то есть оно не будет сохранено в хранилище в месте, где к нему можно будет получить доступ. Возможно ли это?

Как мне расшифровать изображение и затем получить его в формате, в котором оно может быть возвращено в качестве ответа? Спасибо за любую помощь!

Похоже, у вас проблема, потому что self.cipher.decrypt(encrypted_file_content) возвращает необработанные двоичные данные, но вы пытаетесь присвоить их непосредственно encrypted_data[key], который, как ожидается, будет сериализуемым объектом.

Ключевой момент для ее решения:

  1. Расшифрованное изображение должно быть должным образом отформатировано, прежде чем возвращать его в ответ API

  2. Вам нужно вернуть URL-адрес или объект, похожий на файл, который может обрабатывать DRF

Мое предложение состоит в том, чтобы изменить метод to_representation:

class ProfileSerializer(serializers.ModelSerializer):
    # ...

    def to_representation(self, instance):
        encrypted_data = super().to_representation(instance)

        for key in encrypted_data:
            if key == "user_id" or encrypted_data[key] is None:
                pass
            elif key == "profile_picture":
                encrypted_picture = instance.profile_picture
                encrypted_file_content = encrypted_picture.read()
                decrypted_file_content = self.cipher.decrypt(encrypted_file_content)

                # Convert decrypted binary data to Base64 string for API response
                encrypted_data[key] = f"data:image/png;base64,{base64.b64encode(decrypted_file_content).decode()}"
            else:
                encrypted_field = encrypted_data[key]
                encrypted_data[key] = self.cipher.decrypt(encrypted_field).decode("utf-8")

        return encrypted_data

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

Вернуться на верх