How to hide a password in a Python script

python hide password


Background

Python is widely used for web scraping and APIs, dealing with databases, sending emails, and other tasks that may involve credentials, like a username or password. Luckily, there are several ways to hide a password in a Python script, as well as to store multiple passwords for many users if needed. In this post, we’ll talk about three such packages – keyring, passlib, and cryptography, with a focus on the first two.

How to hide a password in a Python script with keyring

keyring is an easy-to-use package that works by allowing you to store a password in your operating system’s credential store. It’s great in that it can work across multiple different operating systems. To get started, just use pip to install:


pip install keyring

Next, let’s import keyring.


import keyring

Once we’ve imported keyring, we can store a username / password combination using the set_password method. This method has three parameters. First, it takes a “servicename”. This is the name we choose for whatever service our username / password is associated with e.g. email, database, etc. In our example, we’ll just call it “test”. Next, the second and third parameters are the username and password, respectively.


keyring.set_password("test", "secret_username", "secret_password")

To retrieve the password, we just need to use the get_password method with the “servicename” value and username.


keyring.get_password("test", "secret_username")

Once we’ve set the password, it remains stored by our operating system – so if you start a new Python session, you’ll be able to retrieve it just the same.

Let’s suppose, for example, that we’re connecting to a database (read more on that here), where we need to pass credentials.


import pyodbc

channel = pyodbc.connect("DRIVER={SQLite3 ODBC Driver};SERVER=localhost;DATABASE=sample_database.db;Uid=secret_username;Pwd=secret_password;'")

We can change this code to use keyring:


import pyodbc
import keyring

channel = pyodbc.connect("DRIVER={SQLite3 ODBC Driver};SERVER=localhost;DATABASE=sample_database.db;Uid=secret_username;Pwd=" + keyring.get_password("test", "secret_username") + ";")


keyring can also be useful when scheduling a Python a script in production that requires a password or credentials.

How to hash passwords with passlib

An alternative way to protecting passwords in Python is hashing. This can be useful if you’re dealing with storing many passwords, such as credentials to a web application (e.g. Flask or Django). Hashing differs from encryption in that encryption works as a 2-way method. Any password that is encrypted can be decrypted. Hashing, on the other hand, works by mapping a value (like a password) to a new, scrambled value. Ideally, there should not be a way of mapping the hashed value / password back to the original value / password.

Let’s install passlib with pip.


pip install passlib

Setting up a CryptContext object

Next, we’ll import the CryptContext class from passlib.context.


from passlib.context import CryptContext

The next step is to setup our CryptContext object. First, in order to do this, we need to decide what hashing algorithm we want to use. There’s multiple possibilities here, but several are specifically recommended by the passlib library:

  • argon2
  • bcrypt
  • pbkdf2_sha256
  • pbkdf2_sha512
  • sha256_crypt
  • sha512_crypt

  • Some of these algorithms require additional packages to be installed. If you try using them without those dependencies, you’ll get a message asking you to install some package first. For example, the argon2_cffi package is required to use the argon2 algorithm above.

    Second, we also need to specify the number of “rounds”. A round is a collection of operations that form a function. This function is then run many times to map our password to a hashed version. A lower number of rounds will mean that whatever you’re hashing is not as secure because less operations are being taken to scramble the information. However, a higher number of rounds will take longer to complete the hash operation. The number of rounds to pick also depends on the algorithm you choose. Choosing the number of rounds for security purposes mostly has to do with slowness i.e. if it’s quick to generate a hash, it’s also faster to brute force figure out the original value. Algorithms like bcrypt and argon2 are slower to produce hashed values, and therefore, usually considered more secure.

    
    
    # create CryptContext object
    context = CryptContext(
            schemes=["pbkdf2_sha256"],
            default="pbkdf2_sha256",
            pbkdf2_sha256__default_rounds=50000
    )
    
    
    # hash password
    context.hash("test_password")
    
    

    In this first example, we’re using the PBKDF2-SHA256 algorithm, as this is a common choice, and typically works without issue on different operating systems. Once the CryptContext object is created, we can hash our password using the hash method like above.

    How to verify if a hashed password is correct

    To verify a password matches its corresponding hash, we can use the verify method. The verify method does not “un-hash” the hashed password, but instead hashes the unprotected password, and compares that to the hashed version.

    
    hashed_password = context.hash("test_password")
    
    context.verify("test_password", hashed_password)
    
    

    Next, let’s look at another example. This time we’ll use the argon2 algorithm. As mentioned above, we need to first install the argon2_cffi package for this. argon2 is a relatively newer algorithm that was designed specifically for hashing passwords.

    
    pip install argon2_cffi
    
    

    Now, we can create our CryptContext object.

    
    # create CryptContext object
    context = CryptContext(
            schemes=["argon2"],
            default="argon2",
            argon2__default_rounds=1000
    )
    
    # hash password
    context.hash("test_password")
    
    

    The cryptography package

    Lastly, let’s talk about the cryptography package. We can install this package using pip:

    
    pip install cryptography
    
    

    Next, let’s import the Fernet class from cryptography. This will allow us to generate a key, which we can then use to encrpyt our password. Note: this process is performing encryption, not hashing. As mentioned above, hashing is recommended because it is generally more secure, but encryption is a little better than storing passwords in plain text.

    
    # import the Fernet class
    from cryptography.fernet import Fernet
    
    # generate key
    key = Fernet.generate_key()
    
    

    Next, we can use our key to encrypt our password using the encrypt method. Note that the password needs to be passed in bytes, like below.

    
    enc = f.encrypt(b"test_password")
    
    

    Lastly, we can decrpyt the encrypted password using the decrypt method.

    
    f.decrypt(enc)
    
    

    Conclusion

    That’s all for this post! In this article, we covered how to hide a password in a Python script using keyring, how to hash passwords using passlib, and the difference between hashing and encryption.

    While you’re here, check out a new online Python course I co-created with 365 Data Science on web scraping and APIs! You’ll learn all about web scraping with Python, how to use APIs, how to scrape JavaScript-rendered content (a must nowadays), and much more! Check it out here!