JustToThePoint English Website Version
JustToThePoint en español
JustToThePoint in Thai

Security in Python

A password vault

A vault is your home base for all your passwords. The idea is to save, access, and manage passwords in one, secure place. The following code is based on How to create a password vault from scratch with Python by NewsCred Dhaka, but it has been modified quite a lot.

import os, subprocess, string, json
from beefish import encrypt_file, decrypt_file
from random import choice, shuffle

class PasswordVault:
    def __init__(self, root_pass=None, filename='secrets'):
        # This is the vault's master key
        self.root_pass = root_pass
        self.filename = filename
        # These are the encripted and decrypted files
        self.encrypted_fp = PasswordVault.get_file_path(self.filename, '.enc', True)
        self.decrypted_fp = PasswordVault.get_file_path(self.filename, '.dec', False)

    @staticmethod
    def get_file_path(filename, extension, encrypted):
        """ It returns the file's path. Observe that the encripted file is hidden (macOS, GNU/Linux)."""
        if encrypted:
            return os.path.join(os.getcwd(), "." + filename + extension)
        else:
            return os.path.join(os.getcwd(), filename + extension)

    @staticmethod
    def secure_password():
        """ It generates a secure password with uppercase and lowercase letters, symbols, and numbers."""
        password = ''.join([choice(string.ascii_lowercase) for i in range(4)])
        password += ''.join([choice(string.ascii_uppercase) for i in range(4)])
        password += ''.join([choice(string.digits) for i in range(2)])
        password += ''.join([choice(string.punctuation) for i in range(2)])
        my_password = list(password)
        shuffle(my_password)
        return ''.join(my_password)
   
    def read(self):
        """ It reads all passwords and stores them in a dictionary. """
        try:
            # decrypt_file returns the encrypted file "encrypted_fp" back to its original unencrypted format "decrypted_fp".
            decrypt_file(self.encrypted_fp, self.decrypted_fp, self.root_pass)
            # We read the unencrypted file.
            content = open(self.decrypted_fp, "r", encoding="ISO-8859-1").read()
            # Once we have already read the unencrypted file, we delete it for security reasons.
            os.remove(self.decrypted_fp)
            # We save the unencrypted file's content in a dictionary. json.loads() takes the file's content and returns the json object which contains data in the form of key/value pairs.
            return json.loads(content)
        except FileNotFoundError:
            return {}

The library beefish does the heavy lifting for us. It provides a simple file encryption using pycrypto: pip install beefish pycrypto.

    def write(self, data={}):
        """ It writes the dictionary "data" with all its key/value pairs (account/password) in the encrypted file "encrypted_fp"."""
        with open(self.decrypted_fp, 'w') as f:
            # json.dump(obj, file) converts the dictionary data to a JSON object and write it into the decrypted file. An indent argument is used to pretty-print JSON to make it more readable. 
            json.dump(data, f, indent=4, separators=(',', ': '))
            f.close()

        # encrypt_file encrypts the file "decrypted_fp" using the master password.
        encrypt_file(self.decrypted_fp, self.encrypted_fp, self.root_pass)
        # Obviously, we need to remove the unencrypted file.
        os.remove(self.decrypted_fp)
    
    def get_password(self, account):
        """ It returns the password of the account, site, app or service given as an argument."""
        data = self.read()

        # If account is not in our dictionary, it generates a new random password and saves it.""" 
        if account not in data:
            password = PasswordVault().secure_password()
            data[account] = password
            self.write(data)

        return data[account]

    def get_all_passwords(self):
        """ It returns all the (account, password) pairs store in our vault."""
        data = self.read()
        return data

    def set_password(self, account, password):
        """ It updates the account, site, app or service's password with a new one. If the account does not exist, it creates a new one."""
        data = self.read()
        data[account] = password
        self.write(data)

    def useSublime(self):
        """ It uses sublime as an editor for managing our passwords."""
        try:
            # decrypt_file returns the encrypted file "encrypted_fp" back to its original unencrypted format "decrypted_fp".
            decrypt_file(self.encrypted_fp, self.decrypted_fp, self.root_pass)
            # We open the unencrypted file with sublime.
            os.system("subl " + self.decrypted_fp)
            # It allows the external program to work.
            input()
            # encrypt_file encrypts the file "decrypted_fp" using the master password.
            encrypt_file(self.decrypted_fp, self.encrypted_fp, self.root_pass)
            # Once we have finished editing the unencrypted file, we delete it for security reasons.
            os.remove(self.decrypted_fp)

        except FileNotFoundError:
            return {}  

    def changeRootPassword(self, nuevaPassword):
    """ It updates the Vault's master key."""
        try:
            # decrypt_file returns the encrypted file "encrypted_fp" back to its original unencrypted format "decrypted_fp".
            decrypt_file(self.encrypted_fp, self.decrypted_fp, self.root_pass)
            # We delete the encrypted file.
            os.remove(self.encrypted_fp)
            # It updates the vault's master pass.
            self.root_pass = nuevaPassword
            # encrypt_file encrypts the file "decrypted_fp" using the new master password.
            encrypt_file(self.decrypted_fp, self.encrypted_fp, self.root_pass)
            # And delete the unencrypted file.
            os.remove(self.decrypted_fp)
        except FileNotFoundError:
            return {} 

if __name__ == '__main__':
    root_pass = subprocess.check_output(['pass', 'MasterPass']) # subprocess.check_output() runs commands with arguments and return their outputs as byte strings. We run "pass MasterPass" that uses Pass.
    root_pass = root_pass.decode("utf-8").rstrip() # We need to decode the byte string to produce a string. We could have just asked the user to introduce the master pass: root_pass = input('Enter the Vault's master password').

Pass is the standard GNU/Linux password manager, a lightweight password manager. We can retrieve our passwords by typing: pass nameAccount, e.g., pass MasterPass.

One of the safest ways to handle your secret keys or passwords is saving them in environment variables. Open your .bash_profile (macOS and GNU/Linux), add: export ROOTPASSWORD = “SecretPassword”, and type the following command for the changes to take effect: source .bash_profile.

On Windows, you need to navigate to Control Panel, System and Security, Advanced System Settings, Environment Variables. To access your variable in your Python code, you need to import the os module and write: print(os.environ.get(‘ROOTPASSWORD’)).

Another alternative is to save your keys and passwords in a separate .env file in your project: ROOTPASSWORD=SecretPassword. Next, we are going to use the python-dotenv library (pip install python-dotenv). It reads key-value pairs from a .env file and can set them as environment variables:

from dotenv import load_dotenv 
load_dotenv() # It takes environment variables from .env. 
root_pass = os.environ.get('ROOTPASSWORD')

You need to make sure to add it in your .gitignore file (.env). It specifies intentionally untracked files that Git should ignore.

    vault = PasswordVault(root_pass) # We create an instance of PasswordVault.
    not_end = True
    while not_end:
        print("Exit. Show (all vault). New (get a random password)")
        inputUser = input("Sublime. Get [account]/Update [account] [new_password]: ").split(" ")

        option = inputUser[0].upper()
        if option == "EXIT":
            not_end = False
        elif option == "SHOW":
            print("It shows all the entries of your personal vault.")
            print(json.dumps(vault.get_all_passwords(), indent=4, sort_keys=True))
        elif option == "NEW":
            print("This is your new secure random password: " + PasswordVault().secure_password())
        elif option == "SUBLIME":
            vault.useSublime()
        elif option == "GET": # The user input is: GET [account]
            account = inputUser[1]
            print(f"The password of {account} is {vault.get_password(account)}")
        elif option == "UPDATE": # The user input is: UPDATE [account] [password]
            account = inputUser[1]
            password = inputUser[2]
            vault.set_password(account, password)

Password generator

Strong passwords are more important than ever. Let’s create a password generator. It creates unique and random passwords.

Password reuse is a problem where people try to remember multiple passwords for everything they interact with on a regular basis. Many people reuse the same password for multiple or all accounts. You can take a “Master Key”, add the name of the service, app, or social media, and create a unique password for every account.

import passwordmeter, sys, subprocess, string
from rich import print
from random import choice

substitutions = (('s', '$'), ('and', '&'), ('b', '8'), ('S', '$'), ('and', '&'), ('B', '8'), ('A', '@'), ('O', '0'), ('I', '1'), ('E', '3'), ('l', '1'), ('L', '1'), ('a', '@'), ('o', '0'), ('i', '1'), ('e', '3'))
misEmojis = [":-(", ":)", ":')", ";(", ":-*", ">:‑)"]

# It adds a random emoji to the password.
def add_emoji(password):
    return password + choice(misEmojis)

# It replaces characters with different replacement characters.
def common_substitutions(password):
    for a, b in substitutions:
        password = password.replace(a, b)
    return password

# Credits: @amillerrhodes, Georgy from stackoverlflow.com
# Caesar is a substitution cypher. It shifts all the characters of the word ("text") by a certain number of places ("shift").
def caesar(text, shift, alphabets):
    def shift_alphabet(alphabet): # This function is defined inside the "caesar" function. It shifts all the characters of the alphabet.
        return alphabet[shift:]+alphabet[:shift]

    shifted_alphabets = tuple(map(shift_alphabet, alphabets)) 

Read our article about Functional Programming in Python, map applies the function shift_alphabet() to each alphabet in alphabets.

    final_alphabet = ''.join(alphabets)
    final_shifted_alphabets = ''.join(shifted_alphabets)
    table = str.maketrans(final_alphabet, final_shifted_alphabets) # The maketrans() function creates a translation table, i.e., a one-to-one mapping of a character to its replacement.
    return text.translate(table)

# It checks the security of our password.
def check_password(myPassword):
    strength, improvements = passwordmeter.test(myPassword) 
    # The main function provided by the passwordmeter package is the Meter.test() method. It returns a tuple of (float, dict).  
    print("It strength is: " + str(strength)) # The float is the strength of the password in the range 0 to 1, where 0 is extremely weak and 1 is extremely strong. 
    print("Its length is: " + str(len(myPassword)))
    print(improvements) # The second parameter is a dictionary of ways the password could be improved.

It uses passwordmeter, a configurable, extensible password strength measuring library.

It adds a root or master key to the password.

def add_rootKey(myPassword):
    root_pass = subprocess.check_output(['pass', 'MasterPass']) 
    # subprocess.check_output() runs commands with arguments and return their outputs as byte strings. We run "pass MasterPass" that uses Pass.
    clave = root_pass.decode("utf-8").rstrip() # We need to decode the byte string to produce a string.
    password = myPassword + clave
    return password

Pass is the standard GNU/Linux password manager, a lightweight password manager. We can retrieve our passwords by typing: pass namePassword, e.g., pass MasterPass.

def cut_password(myPassword, limitSize=24):
# If the password is too long, it shrinks its size.
    if (len(myPassword) > limitSize):
        too_long = len(myPassword) - limitSize
        myPassword = myPassword[:-too_long]
    return myPassword

def create_secure( weakPassword, shift=7, alphabets=[string.ascii_lowercase, string.ascii_uppercase]):
    """ It creates unique and random passwords."""   
    password = caesar(
        sys.argv[1], shift, alphabets)
    print("0. Caesar 7. Your secure password is: [orange]" + password + "[/orange]") 
    password = common_substitutions(password)
    print("1. Replacement. Your secure password is: [yellow]" + password + "[/yellow]") 
    password = str(len(password)) + password[1:-1]
    print("2. Replace the first character by the word's length and remove the last character: [yellow]" + password + "[/yellow]") 
    password = add_rootKey(password)
    print("3. Include the root key: [red]" + password + "[/red]") 
    password = add_emoji(password)
    print("4. Add a random emoji: [pink]" + password + "[/pink]") 
    password = cut_password(password)
    print("5. Reduce the password to 24 characters: [blue]" + password + "[/blue]")
    check_password(password)

if __name__ == "__main__":
    create_secure(sys.argv[1]) # It takes one argument, e.g., "youaretoosweet"

0. Caesar 7. Your secure password is: fvbhylavvzdlla
1. Replacement. Your secure password is: [email protected]@
2. Replace the first character by the word’s length and remove the last character: [email protected]
3. Include the root key: [email protected]
4. Add a random emoji: [email protected]>:‑)
5. Reduce the password to 24 characters: [email protected]
It strength is: 0.8598353623924001 It length is: 24 {‘phrase’: ‘Passphrases (e.g. an obfuscated sentence) are better than passwords’}

Credentials

Storing credentials inside a credentials.py file is the simplest way to store your passwords safely. You can use environment variables, too.

You need to make sure to add it in your .gitignore file (credentials.py). It specifies intentionally untracked files that Git should ignore. It contains the user credentials to access WolframAlpha API

WolframAlpha_app_id = "myPrivateApiKey"

And we can import it like this:

import wolframalpha, requests, credentials
from colorama import Back, Fore, init, Style
# pip install wolframalpha

# It searches in WolfranAlpha.
def wolframAlpha(keyword=''):
  client = wolframalpha.Client(credentials.WolframAlpha_app_id)
  # We send a query, which returns Results objects: 
  res = client.query(keyword)
  # Result objects have pods (a Pod is an answer group from Wolfram Alpha).
  if res['@success'] == 'false':
    print('Question cannot be resolved')
  else: # To query simply for the pods that have ‘Result’ titles or are marked as ‘primary’ using Result.results:
    print(next(res.results).text)
Postman

Postman

 if __name__ == '__main__':
  searchInfo("what is the capital of Italy")

Rome, Lazio, Italy

Adding Salt to Hashing

The best way to protect passwords is to employ salted password hashing. Typically, a user creates an account. Next, his or her password is hashed (a hash algorithm is a function that converts a data string into a numeric string output of fixed length) and stored in a database. When the user comes back, the hash of the password he or she introduces is compared against the hash of the saved password. Obviously, if the hashes match, the user is granted access.

Some password cracking techniques only work because each password is hashed the exact same way. Salting hashes refers to adding random data to the input of a hash function to guarantee a unique output, i.e., a random string is generated and combined with the password and then hashed.

def hash_password(password):
    # uuid is used to generate a random UUID, uuid.uuid4().hex converts a UUID to a 32-character hexadecimal string.
    salt = uuid.uuid4().hex
    # It hashes a password with a salt. encode() returns an utt8-encoded version of the string. 
    # hexdigest() returns the encoded data in hexadecimal format.
    return hashlib.sha256(salt.encode() + password.encode()).hexdigest(), salt

def check_encryption(password, salt, user_password):
    try:
        if (password == hashlib.sha256(salt.encode() + user_password.encode()).hexdigest()):
            return "Success"
        else:
            return "Failure"
    except Exception as e:
        print(e)

if __name__ == "__main__":
    password, salt = hash_password("bananas")
    print("Password: ", password)
    print("Salt: ", salt)
    print(check_encryption(password, salt, "bananas"))

Privacy

PhoneInfoga is one of the most advanced tools to scan phone numbers using only free resources and it is written in Python.

  1. Installation. Download latest release in the current directory:
        curl -sSL https://raw.githubusercontent.com/sundowndev/phoneinfoga/master/support/scripts/install | bash 
    
  2. Use phoneinfoga:
    phoneinfoga scan -n "telephone_number"
    python3 phoneinfoga.py -n "(+42)837544833"
    
Bitcoin donation

JustToThePoint Copyright © 2011 - 2022 Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.