Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to decrypt a value on a different service that it was originally encrypted with Fernet?

I'm playing around with a project that is python backend-based. I'll have Django for the "core" stuff and FastAPI for some crawlers. I'm encrpting some data to the DB with Django using the Fernet module and a custom Field.

class EncryptedField(models.CharField):
    description = "Save encrypted data to DB an read as string on application level."

    def __init__(self, *args, **kwargs):
        kwargs["max_length"] = 1000
        super().__init__(*args, **kwargs)

    @cached_property
    def fernet(self) -> Fernet:
        return Fernet(key=settings.FERNET_KEY)

    def get_internal_type(self) -> str:
        return "BinaryField"

    def get_db_prep_save(
        self, value: Any, connection: BaseDatabaseWrapper
    ) -> Union[memoryview, None]:
        value = super().get_db_prep_save(value, connection)
        if value is not None:
            encrypted_value = self.fernet.encrypt(data=force_bytes(s=value))
            return connection.Database.Binary(encrypted_value)

    def from_db_value(self, value: bytes, *args) -> Union[str, None]:
        if value is not None:
            decrypted_value = self.fernet.decrypt(token=force_bytes(s=value))
            return self.to_python(value=force_str(s=decrypted_value))

Everything work as expected, the problem is when I try to decrypt the value on FastAPI side:

def decrypt(value: bytes):
    return Fernet(FERNET_KEY).decrypt(token=value)

Some important information:

  • I double check and settings.FERNET_KEY == FERNET_KEY, ie, I'm using the same key on both sides.
  • Both services share the same DB and the function are receiving different values when reading for it.
    • Django -> from_db_value -> value -> b"gAAAAABhSm94ADjyQES3JL-EiEX4pH2odwJnJe2qsuGk_K685vseoVNN6kuoF9CRdf2GxiIViOgiKVcZMk5olg7FrJL2cmMFvg=="
    • FastAPI -> user.encrypted_field -> value -> b"pbkdf2_sha256$260000$RzIJ5Vg3Yx8JTz4y5ZHttZ$0z9CuQiPCJrBZqc/5DvxiEcbNHZpu8hAZgmibAe7nrQ=". I actually enter inside the DB and checked that this is the value stored there. user comes from here:
      • from sqlmodel import Session, select
        
        from .models import User
        
        
        async def get_user(db: Session, username: str) -> str:
           statement = select(User).where(User.username == username)
           return db.exec(statement).first()
        

So I'm wondering there's something before from_db_value that's converting the value somehow?!

One final alternative would be to decrypt the value on Django and send it directly to FastAPI, but I'd prefer not to do so.

How can I decrypt the value on FastAPI?

like image 342
Murilo Sitonio Avatar asked Nov 07 '22 00:11

Murilo Sitonio


1 Answers

It is enough to decode the value:

def decrypt(value: bytes):
    return Fernet(FERNET_KEY).decrypt(token=value)

Are you sure you are trying to decrypt the same data? To me, this looks like an encrypted password field and some other field.

mysql> select first_name, password from users_user where login='test'
+------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------+
| first_name                                                                                           | password                                                                                 |
| gAAAAABhSm94ADjyQES3JL-EiEX4pH2odwJnJe2qsuGk_K685vseoVNN6kuoF9CRdf2GxiIViOgiKVcZMk5olg7FrJL2cmMFvg== | pbkdf2_sha256$260000$RzIJ5Vg3Yx8JTz4y5ZHttZ$0z9CuQiPCJrBZqc/5DvxiEcbNHZpu8hAZgmibAe7nrQ= |
like image 152
kjaw Avatar answered Nov 12 '22 15:11

kjaw