Python Secure Password Management: Hashing and Encryption #️⃣🔐✨

python-secure-password-management:-hashing-and-encryption-

When building an application that validates user passwords or needs to store tokens for future use it is critical to not store these values anywhere in clear text. If there is a security breach you want to be confident that your user’s data is protected. Hashing and Encryption are a couple of techniques which can be used to achieve this and we will take a look at how to implement these with Python.

Hashing

If your application needs to allow users to register an account and create a password then you need to store the values they singed up with in order to authenticate them later. Rather than storing the passwords in clear text, this is where a hashing algorithm should be used. Hashing algorithms are one-way functions which produce the same result for the input data but given the output data are nearly impossible to reverse. There are many types of hash algorithms but SHA-256 is a strong and NIST Approved modern algorithm that fits the need of most applications in terms of strength and performance.

Create a simple Python script file to take an input and generate the SHA-256 hash with the hashlib standard library.

📝 hash.py

import hashlib

password = input("Password: ")
password_hash = hashlib.sha256(password.encode("utf-8")).hexdigest()
print(f"Password Hash: {password_hash}")

Run the script and give it a few password inputs.

$ python3 hash.py
Password: test123
Password Hash: ecd71870d1963316a97e3ac3408c9835ad8cf0f3c1bc703527c30265534f75ae

$ python3 hash.py
Password: test123
Password Hash: ecd71870d1963316a97e3ac3408c9835ad8cf0f3c1bc703527c30265534f75ae

$ python3 hash.py
Password: test1234
Password Hash: 937e8d5fbb48bd4949536cd65b8d35c426b80d2f830c5c308e2cdec422ae2244

We can see that the same input produces the same hash result but any change such as an additional character completely changes it. The resulting hash value is what you should store in your database to later validate the user’s password.

Encryption

Hashing is a great option when you do not need use the password value. For use cases where you need to use the actual password value such as storing a long term access token to authenticate on the user’s behalf to an external application then encryption is the best option. Encryption allows you to store the values securely and decrypt them in memory when you need to use them. Python has the cryptography library which includes Fernet Symmetric Encryption to achieve this. Symmetric encryption means we will have a secret key which we can store in our environment variables and use to decrypt stored values.

Install the cryptography and dotenv library.

$ poetry add cryptography python-dotenv
Using version ^41.0.5 for cryptography
Using version ^1.0.0 for python-dotenv

Updating dependencies
Resolving dependencies... (0.1s)

Package operations: 4 installs, 0 updates, 0 removals

  • Installing pycparser (2.21)
  • Installing cffi (1.16.0)
  • Installing cryptography (41.0.5)
  • Installing python-dotenv (1.0.0)

Run an inline python command to generate the Fernet secret key.

$ python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key())"
b'XvYvP_c4gBDLCLbjgz6Hc47ND_BcoMYt3Cz5pAKx1qQ='

Add this value to a .env file.

📝 .env

SECRET_KEY=XvYvP_c4gBDLCLbjgz6Hc47ND_BcoMYt3Cz5pAKx1qQ=

Create a new python script which will load our secret key from the environment variables, instantiate the Fernet client with the key, and allow a new password to be encrypted and stored in a simple text file or print out the decrypted value of an existing stored password.

📝 encrypt.py

import os
import sys

from cryptography.fernet import Fernet
from dotenv import load_dotenv

load_dotenv()

SECRET_KEY = os.getenv("SECRET_KEY")
assert SECRET_KEY
FERNET = Fernet(SECRET_KEY)

if len(sys.argv) > 1 and sys.argv[1] == "decrypt":
    with open("pw.txt") as f:
        stored_password = f.read()

    stored_dec_password = FERNET.decrypt(stored_password).decode()
    print(f"Decrypted Password: {stored_dec_password}")
else:
    new_password = input("New Password: ")
    new_enc_password = FERNET.encrypt(new_password.encode()).decode()

    with open("pw.txt", "w") as f:
        f.write(new_enc_password)

    print(f"Encrypted Password Stored: {new_enc_password}")

Test it out to validate it is working as expected.

$ python3 encrypt.py
New Password: Test123!!
Encrypted Password Stored: gAAAAABlR7V0TLTZMT_ZHEoPtqbW3B9LYgohYdUNG6Lukx9M2NSLgrFN6MUZKCNPP3Hq_KuuEPpJPPqqIktUkZTBh3qenKnQAA==

$ python3 encrypt.py decrypt
Decrypted Password: Test123!!

Awesome! 🎉 This shows the core concepts of encrypting/decrypting values and in a production environment rather than storing them in a simple text file you would just store and retrieve the values from a database.

I hope you have found this article helpful for building your next amazing (and secure) application! 😊

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
what’s-new-in-stateadapt-20.0:-adapt-rework

What’s New in StateAdapt 2.0.0: adapt rework

Next Post
useref-helps-you-to-avoid-re-rendering-of-the-component

useRef helps you to avoid re-rendering of the component

Related Posts