Decrypting image fields for Django REST API responses
I am trying to implement a system in which pictures are encrypted on my database, but when they are sent as a response to a user, this data becomes decrypted. Here is how I am encrypting...
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
This code is called whenever an object is going to be saved to the database. It encrypts all the fields, and deals with the image case.
Now, I need to decrypt this data for my response. My first thought was to manipulate the to_representation
on the serializer such that I could decrypt each data part. This works great for text fields, but I haven't figured out a way to do it with image fields! This is the code so far...
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
When I call encrypted_data[key] = decrypted_picture
, I get the error message...
'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
This makes sense, but I'm not sure how to solve it exactly. What I am looking to ultimately achieve with this implementation too is that the decrypted image is only available on API calls - that is, it isn't saved to storage in a place where it can get accessed. Is that possible?
How should I go about decrypting an image and then getting it in a format in which it can be returned as a response? Thank you for any help!