Generating a Secure Password, Saving It as a CSV, and Password Protecting a File in Python

Posted in Web Development on August 2022

I recently wrote Generating a Secure Password and Saving It to a CSV Spreadsheet in Python, which shows how to create a secure password and save it to a CSV spreadsheet. I wanted to take it a step further for those paranoid, maybe rightfully so, people who want more security on their local computer. This tutorial not only shows how to generate a secure password, save it to a CSV file, but also password protecting that file. One thing to note is that text files (including CSV) cannot be password protected. In order to achieve this, the file needs to be zipped.

Python Code

import string
import secrets
import csv
import pyminizip
import os

while True:

    # Getting letters, numbers, and special characters
    characters = string.ascii_letters + string.punctuation + string.digits

    # Randomizing
    password =  "".join(secrets.choice(characters) for x in range(16))

    title = input("\nTitle of Password:\n>>")

    print("Title:\n" + title +"\n\nPassword:\n"+ password)

    mkfile = open("test.csv", "a", encoding="utf-8")

    with mkfile as file:

        writer = csv.writer(file)

        # Writes the title
        writer.writerow([str(title)])

        # Writes the generated password
        writer.writerow([str(password)])

        # Creates a new row
        writer.writerow("\n")

        # Repeat? y/n
        choice = input("\nEnter another? [y/n]")

        if choice == "y":
            continue
        else:
            mkfile.close()
            
            # Targeted file
            file = "test.csv"

            # Output of zip file
            output = "not-the-file-youre-looking-for.zip"

            # Password
            password = "123"

            # Compression level
            compress_lvl = 5

            # Compress file
            pyminizip.compress(file, None, output, password, compress_lvl)
            
            # Remove the orginal file
            os.remove("test.csv")

            break

Digging Into the Code

Lines 1-5: Five modules, string, secrets, csv, pyminizip, and os are required, and I’ve linked to each of their documentation if further reading is needed. Python recommends using secrets in lieu of the random module noting: “In particular, secrets should be used in preference to the default pseudo-random number generator in the random module, which is designed for modelling and simulation, not security or cryptography.” Meanwhile pyminizip is used to create a password encrypted zip file and os to remove the original CSV.

Line 7: The entire Python code is wrapped in a while True loop, which will run as long as the conditional expression evaluates to True. This is to allow the user to enter more generated passwords, which will be found on line 35.

Lines 10 and 13: These are variables to be used later. The characters variable makes use of the string module to use ascii_lowercase and ascii_uppercase constants, punctuations (e.g. !”#$%&'()*+,-./:;<=>?@[\]^_`{|}~), and digits (e.g. 0123456789). Combining these three constants will help create a strong password. To make this much more secure, the secrets module is used to randomize the password in 16 characters.

Line 15: The title variable is used to help the user to correlate what the password is referencing.

Line 17: In order for the user to identify their password, Python prompts “Title of Password:”. Once a title is entered, a password will be generated.

Line 19: The variable mkfile is created to work within the object responsible for converting the user’s data into delimited strings. Firstly, it’s going to open() a file, followed by a file name/location. Next, "a" is used, which opens a file for appending. If the file doesn’t exist, it creates a new file for writing. I see a lot of people using "w", which opens a file for writing only and overwrites the file if the file exists. In this case, I don’t want to blow away my data every time I update, so I use 'a'. Lastly, encoding="utf-8'" makes sure the data is encoded properly.

Lines 21-32: To write to a CSV file in Python, we use the csv.writer() function. This function writes the user’s data into a delimited string. This string can then be used to write into CSV files using the writerow(). writer.writerow([str(title)]) and writer.writerow([str(password)]) store the variables into the CSV spreadsheet. Using str() allows the data to be in one cell. If that was removed, each character would be on its own cell.

Lines 35-38: The user is asked whether they want to add more entries or quit. A simple if will determine either repeating the loop or moving onto else.

Lines 39-55: This is where the code differs from Generating a Secure Password and Saving It to a CSV Spreadsheet in Python. Firstly, mkfile.close() will close the CSV file (e.g. test.csv). If this isn’t done, then the last generated password will be omitted. file, output, password, and compress_lvl are variables to be used for pyminizip’s compression. Here’s an example from their website: pyminizip.compress(“/srcfile/path.txt”, “file_path_prefix”, “/distfile/path.zip”, “password”, int(compress_level)). file is targeting the file to be compressed, output will be the name of the zip file, None is the prefix path (string) or None (path to prepend to file), password creates the password, and compress_lvl compresses the file between 1 to 9 (faster to more compression).

Line 58: To make this a proper secure technique, the original file is removed.

Conclusion

I put both files, the python file that runs the code and the original CSV (test.csv), in the same folder for demonstration purposes only. This process could all be rendered worthless if the python and targeted CSV files are located in the same folder—someone could easily find your password. This can be changed within the file variable.

I already wrote a similar tutorial, but I figured there would be someone saying that saving it to your local machine isn’t secure enough—maybe rightfully so. Regardless, I wrote this for several reasons. First, most examples of Python generated passwords aren’t making use of the string module properly (e.g. string.ascii_letters + string.punctuation + string.digits). I’ve seen a lot of people doing something like digit = “123456789” in their code–not realizing that the string module can already take care of that. Second, I’ve read debates regarding using random or secrets, but I prefer to stick with Python’s documentation and use the latter module. Third, I haven’t found examples where people give some association to their passwords. A password generator is useless, if there’s no correlation between account and password. Last, if you’re generating secure passwords, you’ll have to store that information to find later. Why not fix that and insert the data into a local file.

Back To Top