In short: I want to store strings encrypted in the database and decrypt them when i need them. What is the best practice for this?
Context: I am building an application that uses a 3rd party API that requires a login and password. I want to store the credentials that will be used for the 3rd party API in the database as their will be many different credentials. I was looking on the django documentation and on stackoverflow but i only find things that hash passwords.
EDIT: I did some more searching and i realy liked this: https://github.com/georgemarshall/django-cryptography
Anyone some other ideas or opinions on the repository i found?
You should create a custom model field and override the from_db_value() and get_prep_value to encrypted and decrypt the string. You can use many packages to do the encryption algorithm.
With cryptography you can do something like:
from django.db.models import CharField
from cryptography.fernet import Fernet
from django.conf import settings
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
class SecureString(CharField):
"""Custom Encrypted Field"""
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend())
key = base64.urlsafe_b64encode(kdf.derive(settings.SECRET_KEY))
f = Fernet(key)
def from_db_value(self, value, expression, connection):
return f.decrypt(value)
def get_prep_value(self, value):
return f.encrypt(value)
Note: You should abstract the encryption logic for a more optimal solution.
This ended up being how I adapted the script to make it work properly with the previous fields where I had insecure credentials. I had to make some adjustments based on this
class SecureCharField(CharField):
"""
Safe string field that gets encrypted before being stored in the database, and
decrypted when retrieved. Since the stored values have a fixed length, using the
"max_length" parameter in your field will not work, it will be overridden by default.
"""
def __init__(self, *args: Any, **kwargs: Any):
kwargs['max_length'] = 512
super().__init__(*args, **kwargs)
salt = bytes(os.environ["SECURE_STRING_SALT"], 'utf-8')
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend()
)
# Encode the FERNET encryption key
key = base64.urlsafe_b64encode(kdf.derive(
bytes(os.environ['FERNET_ENCRYPTION_KEY'], 'utf-8')
))
# Create a "fernet" object using the key stored in the .env file
f = Fernet(key)
def from_db_value(self, value: str, expression: Any, connection: Any) -> str:
"""
Decrypts the value retrieved from the database.
"""
value = str(self.f.decrypt(bytes(value, 'cp1252')), encoding='utf-8')
return value
def get_prep_value(self, value: str) -> str:
"""
Encrypts the value before storing it in the database.
"""
value = str(self.f.encrypt(bytes(value, 'utf-8')), 'cp1252')
return value
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With