Almost every application requires some form of authentication, password processing, or the use of security credentials (such as API keys). You may not be a security expert, but you should know how to securely handle all these passwords and credentials to protect your application users’ credentials and data as well as your own API keys and various tokens.

Securing these security elements includes generating them, validating them, storing them securely, and protecting them from enemies. So in this article, we’ll explore Python libraries, tools, and concepts that will serve the same purpose!

Tip password

Let’s start simple — you have a basic Python application with a command line interface. You need to ask the user for a password. You can use input(), but this will display the password in the terminal to avoid using getPass instead:

import getpassuser = getpass.getuser()password = getpass.getpass()# Do Stuff...
Copy the code

Getpass is a very simple package that allows you to prompt the user for a password and get their username by extracting the current user’s login name. Be aware, however, that not every system supports hiding passwords. Python will attempt to warn about this, so simply read the warning on the command line.

generate

Sometimes, it is better to generate a password than to prompt the user for one. For example, if you want to set the initial password that changes when you log in for the first time. Finally, if your time is not very tight, and you want to improve Python quickly, the most important thing is that you are not afraid to work hard, I suggest you to pay @762459510, that is really good, many people progress quickly, need you not afraid to work hard! You can go to add a look at ~

There is no library to generate a password, but implementing it is not difficult:

import stringimport secretslength = 15
# Choose wide set of characters, 
but consider what your system can handlealphabet = 
string.ascii_letters + string.digits + string.punctuationpassword = ''
.join(secrets.choice(alphabet) 
for i in range(length))
Copy the code

Passwords generated using the code above will be strong, but hard to remember. If it’s just an initial, temporary password or a temporary token, then it’s fine, but if the user should use a longer password, then a password is more appropriate.

We could build a password generator as we did with simple passwords above, but why bother if there are libraries available? The library, called XKCDPass after XKCD for password strength, does exactly what the cartoon describes — producing strong passwords made up of words:

# pip install xkcdpassfrom xkcdpass import xkcd_password as xpword_file = 
xp.locate_wordfile()words = xp.generate_wordlist(wordfile=word_file, min_length=5, max_length=10)
for i in range(4):    
print(xp.generate_xkcdpassword(words, acrostic="python", numwords=6, delimiter="*"))
# punch*yesterday*throwback*heaviness*overnight*numbing
# plethora*yesterday*thigh*handlebar*outmost*natura
l# pyromania*yearly*twisty*hyphen*overstuff*nuzzle
# pandemic*yearly*theology*hatching*overlaid*neurosis
Copy the code

This fragment first finds a word/dictionary file on your system, such as /usr/dict/words, and selects all words of the specified length, from which it generates a list of words used to generate a passphrase. The generator itself has some parameters that we can use to customize a passphrase. In addition to the obvious word count and length, it has

acra

Parameter, which word character will be used as the first letter of the word in the password (sound complicated?) Well, see the password example above).

If you really want to build it yourself, rather than adding dependencies to your project, you can do so in the Python documentation.

hash

Now that we’ve asked the user for a password, or generated one for them, what do we do with it? We might want to store it somewhere in the database, but you probably (hopefully) know that you shouldn’t store passwords in clear text. Why is that?

Passwords, then, should not be stored in a recoverable format, whether plain text or encrypted. They should be hashed using strongly encrypted one-way functions. Thus, if someone gets hold of the passwords in the database, they will have a hard time recovering any actual passwords, because the only way to recover any passwords from the hash is to force — that is — use possible plaintext passwords, hash them with the same algorithm, and compare the results to the entries in the database. Finally, if your time is not very tight, and you want to improve Python quickly, the most important thing is that you are not afraid to work hard, I suggest you to pay @762459510, that is really good, many people progress quickly, need you not afraid to work hard! You can go to add a look at ~

To make brute strength harder, and

salt

Should be used. A SALT is a random string stored next to a hashed password. It is appended to the password before hashing, which makes it more random and therefore hard to guess (use rainbow table).

However, since modern hardware can attempt billions of hashes per second, it’s not enough that the password is hard to guess.

slow

The hash function is used for password hashing, making it much less efficient for an attacker to force a password.

Note: The above greatly simplifies the logic and reasons for using these hash functions. For more thoughtful explanations, see the article.)

There are quite a few libraries and separate hashing algorithms, but the above requirement Narrows our choices considerably. The solution for hashing in Python should be Passlib because it provides the right algorithm, as well as a high-level interface that can be used even by people who are not very proficient in cryptography.

# pip install passlibfrom passlib.hash import bcryptfrom getpass import getpassprint(bcrypt.setting_kwds)
# ('salt', 'rounds', 'ident', 'truncate_error')print(bcrypt.default_rounds)
# 12hasher = bcrypt.using(rounds=13)  
# Make it slowerpassword = getpass()hashed_password = hasher.hash(password)print(hashed_password)
# $2b$13$H9.qdcodBFCYOWDVMrjx/uT.fbKzYloMYD7Hj2ItDmEOnX5lw.BX.
# __// ____________________/_____________________________/
# Alg Rounds  Salt (22 char)            
Hash (31 char)
print(hasher.verify(password, hashed_password))
# Trueprint(hasher.verify("not-the-password", hashed_password))
# False
Copy the code

Bcrypt is our algorithm of choice in the snippet we used, as it is one of the most popular and well-tested hashing algorithms. First, we examine its possible Settings and check the default number of rounds used by the algorithm. Then change

The hill

Using more turns (cost factor) makes hashing slower, so hashing is harder to crack. This number should be maximum and not cause intolerable delays for your users (~300 ms). Passlib updates the default loop value periodically, so you don’t necessarily need to change this value.

After HASHER is ready, we prompt the user for a password and hash it. At this point, we can store it in the database and continue to validate it with the original plaintext password for demonstration purposes.

From the code above, we can see that passlib boils down to hash and modify the methods selected by our algorithm. However, if you want to have more control over plans, turns, etc., then you can use the CryptContext class:

from passlib.context import CryptContextctx = CryptContext(schemes=
["bcrypt", "argon2", "scrypt"],                   
default="bcrypt",                   
bcrypt__rounds=14)password = getpass()hashed_password = ctx.hash(password)print(hashed_password)
# $2b$14$pFTXqnHjn91C8k8ehbuM.uSJM.H5S0l7vkxE8NxgAiS2LiMWMziAeprint
(ctx.verify(password, hashed_password))
print(ctx.verify("not-the-password", hashed_password))
Copy the code

This context object allows us to work with multiple scenarios, set default values, or configure cost factors. This may not be necessary if your application authentication is simple, but if you need the ability to use multiple hashing algorithms, disrecommend them, rehash hash, or similar advanced tasks, then you may want to review the full text. CryptContext integration tutorial.

Another reason you might want to use CryptContext if you need to handle operating system passwords such as /etc/shadow. To do this, you can use passlib.hosts, see the example for more information. here

For completeness, I’ve listed several other libraries available, including their (different) use cases:

  • Bcrypt is the library and algorithm we used above. This is the same code used by: Passlib has no real reason to use this low-level library.
  • Crypt is a Python standard library module that provides cryptographic hashes. However, the algorithms provided depend on your system, and the ones listed in the documentation are not as strong as those shown above.
  • Hashlib is another built-in module. However, this one contains powerful hashing capabilities suitable for password hashing. The library’s interface makes functions more customizable, so more knowledge is required to use them correctly (and safely). You can definitely use functions from this module, such as hashlib.scrypt your password.
  • HMAC, the last hashing module provided by the Python standard library, is not suitable for password hashing. HMAC is used to verify message integrity and authenticity and does not have the properties required for cryptographic hashing.

Note: With your newfound knowledge of the proper way to store passwords, let’s imagine that you forget your password for some service. You click on the

“Forgot your password?”

On the site, instead of recovering the link, they give you the actual password. This means they store your password in clear text, which also means you should escape the service (and change it if you use the same password elsewhere).

Safe storage

In the previous section, we assumed that the purpose was to store other users’ credentials, but what about your own password that you used to log in to a remote system?

Keeping the password in the code is obviously a bad choice, because it is provided in clear text for anyone to see, and you can accidentally push the password onto gitrepo. A better option is to store it in environment variables. You can create a.env file, add it to.gitignore, and populate it with the credentials required for the current project. You can then package all these variables into your application using Dotenv, as follows:

# pip install python-dotenvimport osfrom os.path import join, 
dirnamefrom dotenv import load_dotenvdotenv_path = join(dirname(__file__), ".env")
load_dotenv(dotenv_path)API_KEY = os.environ.get("API_KEY", "default")print(API_KEY)
# a3491fb2-000f-4d9f-943e-127cfe29c39c
Copy the code

This fragment is first built into the. Env file using the os.path functions, which are then used to load environment variables. Load_dotenv (). If your.env file is in the current directory, as in the example above, you can simplify the code by calling load_dotenv(find_dotenv()) to automatically find the environment file. When loading the file, all that is left is to use os.environ.get.

Or, if you don’t want to contaminate your environment with application variables and secrets, you can load them directly like this:

from dotenv import dotenv_valuesconfig = dotenv_values(".env")
print(config)
# OrderedDict([('API_KEY', 'a3491fb2-000f-4d9f-943e-127cfe29c39c')])
Copy the code

The above solution is good, but we can do better. Instead of storing passwords in an unprotected file, we can use the system’s

Key ring

, the application can store security credentials in an encrypted file in the home directory. By default, the file is encrypted with the user account login password, so it will be unlocked automatically upon login, so you don’t have to worry about additional passwords.

To use keyring credentials in a Python application, we can use the name keyring:

# pip install keyringimport keyringimport 
keyring.util.platform_ as keyring_platformprint(keyring_platform.config_root())
# /home/username/.config/python_keyring  
# Might be different for youprint(keyring.get_keyring())
# keyring.backends.SecretService.Keyring (priority: 5)
NAMESPACE = "my-app"ENTRY = "API_KEY"keyring.set_password
(NAMESPACE, ENTRY, "a3491fb2-000f-4d9f-943e-127cfe29c39c")print(keyring.get_password(NAMESPACE, ENTRY))
# a3491fb2-000f-4d9f-943e-127cfe29c39ccred = keyring.get_credential(NAMESPACE, ENTRY)
print(f"Password for username {cred.username} 
in namespace {NAMESPACE} is {cred.password}")
# Password for username API_KEY in namespace my-app is a3491fb2-000f-4d9f-943e-127cfe29c39c
Copy the code

In the code above, we first examine the location of the KeyRing configuration file, which is where you can make some configuration adjustments as needed. We then examine the active keyring and continue to add a password to it. Each entry has three attributes –

Service, username, and password, where the service acts as a namespace, which in this case will be the name of the application. To create and retrieve entries, we simply use set_password and get_password, respectively. In addition, get_credential can be used – it returns a credential object with attributes of a username and password. Finally, if your time is not very tight, and you want to improve Python quickly, the most important thing is that you are not afraid to work hard, I suggest you to pay @762459510, that is really good, many people progress quickly, need you not afraid to work hard! You can go to add a look at ~

Closed mind

Even if you are not a security expert, you are still responsible for the basic security features of the applications you build. This includes good handling of user data, especially passwords, so hopefully these examples and recipes will help you do just that.

In addition to the methods and techniques shown in this article, the best way to handle passwords is to avoid using them entirely, either by delegating authentication to an OIDC provider (such as Google or GitHub), or by replacing them with key-based authentication and encryption, which we’ll discuss in the next article.