Расшифровка полей изображений для ответов 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]
, который, как ожидается, будет сериализуемым объектом.
Ключевой момент для ее решения:
Расшифрованное изображение должно быть должным образом отформатировано, прежде чем возвращать его в ответ API
Вам нужно вернуть 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 и не сохраняется.