Saving Passwords with Relative Security [A Guide for Dummies]

Written by wbk#!$4 | Published 2020/05/04
Tech Story Tags: passwords | crypto | pasword-protection | password-security | python3 | tutorial-for-beginners | programming | hackernoon-top-story

TLDR This article is recommended not only for novice programmers, but for users who want to know why the hell they need a humanly unpronounceable password. We will use Python 3 to create a user creation program as simply as possible. The security of a system is proportionally inverse to its practicality of use. The more secure a password is, the more complex it will be. The more complex a password will be the more secure it is. We will arbitrarily decide that a decent password needs:. At least six characters;.At least two special characters;. At least 14 characters minimum;.via the TL;DR App

There is a mystical aura around password creation. Some kind of occult knowledge reached only by the distant cryptographic hermits who ascended to nirvana after decades of meditation on the Patagonian glaciers and liters of instant coffee. In this article, we will try to translate a few drops of this ancient wisdom so that we, mere mortals, have secure accounts to store pictures of cats and e-books that we will never read.
First of all, this article is recommended not only for novice programmers, but for users who want to know why the hell they need a humanly unpronounceable password.

Requirements:

If you are reading this article, I assume you are familiar with programming languages or, at least, with their logic. We will use Python 3 to create a user creation program as simply as possible. If you are a beginner, please read about data entry and exit before proceeding.
In the beginning, God created a to-do list, and human beings, in his image and likeness, tend to copy this strategy. This is what we need to do:
  • Save the user name;
  • Ensure that his password is secure;
  • Save the password securely;
  • Rest on the seventh day;
  • The Holy Name of the User [Don't forget it, please]

    There are no secrets here. Let the user be called the way they prefer, it is not our duty to judge him. Let's take the opportunity to get our basic class ready. The digital representation of God's Ctrl + C/Ctrl + V:
    class User:
    
        def __init__(self, user, passwd, salt):
            self.username = user
            self.passwd = passwd
            self.salt = salt
    Don't worry about "salt". We will talk about it soon.

    Is Your Password Worthy?

    I would love to talk about how a code of less than forty lines can generate millions of passwords per second, pull hidden words from the depths of your soul and test them with the same ease with which we procrastinate on Netflix or check our email. However, stick to what is strictly necessary. Let us leave the whys to the philosophers, drunks and beggars.
    Safety <----------------------------------------------------------------------> Practicality
    The stunningly beautiful hieroglyph above [found in the gold halls of Eldorado, South America] describes how advanced societies calculated the need for their passwords. They found that the security of a system is proportionally inverse to its practicality of use. That is, pulling the coal to our sardine, the more secure a password is, the more complex it will be.
    In our study, we will arbitrarily decide that a decent password needs:
  • At least six characters;
  • At least six digits;
  • At least two special characters;
  • Which leaves us with fourteen characters minimum;
Let's create the method that will receive the password and check the level of dignity (For the sake of simplicity, we are not distinguishing between uppercase and lowercase characters.):
    def check_passwd(self, passwd):
        """ Check if is a valid password """
        while True:
            # Check Size
            if len(passwd) < 14:
                print("This password is too short.")
                break            

            # Check Ascii Letters
            counter = 0
            for i in passwd:
                if i in self.passwd_characteres:
                    counter += 1
            if counter < 6:
                print("Your password need at last six (6) characteres.")
                break

            # Check Digits
            counter = 0
            for i in passwd:
                if i in self.passwd_numbers:
                    counter += 1
            if counter < 6:
                print("Your password need at last six (6) digits.")
                break

            # Check Special Characteres
            counter = 0
            for i in passwd:
                if i in self.passwd_special:
                    counter += 1
            if counter < 2:
                print("Your password need at last two (2) special characteres.")
                break

            # The password has passed!
            return True
You may have noticed that this method belongs to a class. We created a manager to test these functions. We are only discussing the main functions, the complete code will be available at the end.

The Password Seems Worthy. And now?

"Seems" is exactly the word. Nothing prevents the user from typing
aaaaaa000000 **
or
@abcedf123456@
, or anything like that. We are taking on the role of enlightened beings and therefore do not trust the user. If his password is weak it is our system that will be insecure, not just his data. Damn users! Fortunately, in Python, there is a secret called secrets:
import secrets
Everything you need to know about it is here. (#ReadtheDocs) Let's get straight to the point. That's where our "salt" comes in. "Salt" is a pinch, a little thing that we add to the user's password to ensure it is secure. We can generate it automatically using the secrets module. It provides several types of tokens, particularly I prefer this one:
# 32 is the number of bytes of the text to be generated.

secrets.token_urlsafe(32)
You can add the salt at the beginning, in the middle or at the end of the password. Here, we will use the end and the password will end more or less like this:
"aaaaaa*000000*ALE_tEwJXs87D-yfgU-D7DWbASX5g_D-oENWUIBi1q8"
If someone can figure this out through trial and error, please give him all of your other passwords. He deserves it for the effort.

Are we done? There is an Episode Waiting for Me.

No. Bring the mouse pointer back and read. Now we have a reasonably secure password, how should we save it? It's time to activate the last trap card in the article: you must know something about hashes before continuing. If you don't know, roll the dice with disadvantage and try again later. There are a few reasons why you should encrypt a password before saving it.
The most important is that if an attacker has access to the database, he will not have the passwords available immediately. Therefore, even if a large part is compromised, he will not be able to access the accounts and their respective data. Finally, let's turn the potentially suspicious password into a nightmare for spy decoders:
worthy_password = hashlib.sha256((password + salt).encode()).hexdigest()
Stay strong, life won't wait for you. We are finishing.
In the end, God made another list. One about the completed tasks. He (or us) had a username, a "salt" for this user and an encrypted password. We saved this separately, as the User class allowed us. We saved the salt so that, in an attempt to login, we add it to the password entered exactly as we did at creation and check if the hashes are identical. If they are, the password is correct and we will have no complications for the next few minutes. Or until the user forgets the password again, whichever comes first.
So, we're done. Now we understand, now we get it. Now all the pieces fit together and we feel stupid for not having noticed before how the gears turn and the world rotates. Below is the complete code, organized in a manager ready to be tested. For more drops of Patagonian wisdom - or excerpts of guessing - access the Prometheus Prototype on Telegram.
# ================================================================== #
# The **basics** of how to save a reasonably secure username and
# password.
# ================================================================== #
#  DEPENDENCIES
# ================================================================== #
import hashlib
import secrets
import string

# ================================================================== #
#  USER CLASS
# ================================================================== #
class User:

    def __init__(self, user, passwd, salt):
        self.username = user
        self.passwd = passwd
        self.salt = salt

# ================================================================== #
#  MANAGER CLASS
# ================================================================== #
class Manager:

    def __init__(self):
        self.users = []
        self.passwd_characteres = string.ascii_letters
        self.passwd_numbers = string.digits
        self.passwd_special = string.punctuation

    def input_username(self):
        """ Gets the Username """
        return input("Enter your user name: ")

    def input_passwd(self):
        """ Gets the Password """
        return input("Enter your user name: ")

    def check_username(self, new_username):
        """ Check if is a valid Username """
        for user in self.users:
            if user.username == new_username:
                print("This username already exists.")
                return True
        return False

    def check_passwd(self, passwd):
        """ Check if is a valid password """
        while True:
            # Check Size
            if len(passwd) < 14:
                print("This password is too short.")
                break            

            # Check Ascii Letters
            counter = 0
            for i in passwd:
                if i in self.passwd_characteres:
                    counter += 1
            if counter < 6:
                print("Your password need at last six (6) characteres.")
                break

            # Check Digits
            counter = 0
            for i in passwd:
                if i in self.passwd_numbers:
                    counter += 1
            if counter < 6:
                print("Your password need at last six (6) digits.")
                break

            # Check Special Characteres
            counter = 0
            for i in passwd:
                if i in self.passwd_special:
                    counter += 1
            if counter < 2:
                print("Your password need at last two (2) special characteres.")
                break

            # The password has passed!
            return True

    def protect_passwd(self, passwd):
        """ Encrypt the password before save it. """
        salt = secrets.token_urlsafe(32)
        encripted_passwd = hashlib.sha256((passwd + salt).encode()).hexdigest()
        return (encripted_passwd, salt)

    def create_new_user(self):
        """ Create a new user. """
        while True:
            user = input("Type your username: ")
            if not self.check_username(user):
                break
        while True:
            passwd = input("Type your password: ")
            if self.check_passwd(passwd):
                pass_pack = self.protect_passwd(passwd)
                break

        # Well done!
        new_user = User(user, pass_pack[0], pass_pack[1])
        self.users.append(new_user)
        print("\nWellcome to this database!")

# ================================================================== #
# We're done here. To load the user safely, simply add the 'salt'
# saved in the profile to the password entered at the time of login.
# ================================================================== #

# TESTING:
manager = Manager()
manager.create_new_user()

# Let's see:
print("\n")
print("User name: " + manager.users[0].username)
print("Saved password: " + manager.users[0].passwd)
print("This-user salt: " + manager.users[0].salt)
print("\n")
input("Press anything to quit.")

Written by wbk#!$4 | Writer, editor, hunter of flying krakens and, sometimes, paranoid with security and privacy.
Published by HackerNoon on 2020/05/04