How to do Passwords Right
If you've ever made a text-based RPG, chat application, or easter-egg filled number guessing game, you've probably collected a password or some kind of secret passphrase from your user, keeping it away from potential prying eyes. So I bring you a couple tips from my experience building console programs.
Quick note: I will be using Python and its standard libraries for this tutorial. If you are using Node or Scheme or Assembly or BrainF, this should be fairly easy to adapt. Other note: This tutorial is mainly geared to beginners, so I simplied some things. If you are a more advanced user, please bear with me.
Let's get started! Here is how you are likely getting password from a user:
password = input("Please enter your password > ") if password == "my_secret_password": print("Many secrets are revealed!")
Works fine, but there are actually two issues with this.
First, here's what it looks like when the user enters their password:
Please enter your password > my_secret_password Many secrets are revealed!
Let's imagine that a malicious user (let's call him Brian) is trying to steal your password. If he is looking over the users shoulder, he can see the users password plain as bun without a vegetarian patty and a slice of cheddar cheese! And if your user and Brian both have to enter their passwords in the same program, Brian can simply scroll up and read the user's password.
Fortunately for us, this is a simple problem to fix. In Python, there is a built-in module called
getpass. If you are new to modules, they are essentially pieces of code that you can add to your code to give it extra functionality. The
getpass module has only one function in it, called, oddly enough,
getpass. To import this function add
from getpass import getpass on a new line to the top of your program to add the
getpass function to your program.
getpass is almost exactly like
input, except for one key difference. Instead of just putting your prompt right between the parenthesis, append
prompt= before the string. If you're interested, Google "python keyword arguments".
So, with our new knowledge, here is our new program:
from getpass import getpass password = getpass(prompt="Please enter your password > ") if password == "super_great_password": print("Tons of secrets over here!")
"But wait!", you say, realizing the fatal flaw in the plain "Brian is intelligent, and knows Python! He can read the right password within my code." Fortunately, there is also a fix to this. But first, we have to talk about hashes.
Hashes are way of turning a piece of text into a long, random-looking string. Every piece of text has its own unique hash, but you can't get find a string from a hash. All you can do is check if a hash is made with a specific piece of text.
So here is what we have to do - store a hashed password instead of a plaintext password. Then, when we get the password, hash that as well, and compare the two. If that sounds complicated, it turns out to be pretty simple. I won't go into great detail on initially generating hashes from passwords, but I wrote a tool that you can find at the end of the article to generate password hashes, and if you're curious, it's pretty easy to dissect. So let's start out our sample program with our hash (it's a string, so in quotes):
correct_password = # your hash goes here in quotes
Python comes to the rescue once again with its module,
hashlib, which allows for the creation of hashes. For the purposes of this tutorial, we will be using the SHA256 hashing algorithm, as it is pretty difficult to crack and is fast for a hashing algorithm. If you need something completely cryptographically sound, check out Python's
hashlib documentation for more hashing algorithms.
hashlib comes with the aptly-named function called
sha256. Let's import that now with
from hashlib import sha256 at the top of our file.
sha256 takes only one argument, in binary text form (you can turn any string into binary text form by appending
.encode() outside of the quote marks), which it then hashes. This function returns a hash object, but to make much use of it, we need to turn it back into text by appending
from getpass import getpass from hashlib import sha256 correct_password = # lengthy hash password = sha256(getpass(prompt="Enter password: ").encode()).hexdigest()
Okay! With me so far? Now we just need to compare the hashed password we've got with our stored hash from earlier. Thus, here is the final program, with all my recommended adjustments.
from getpass import getpass from hashlib import sha256 correct_password = # hash goes here in quotes password = sha256(getpass(prompt="Enter password: ").encode()).hexdigest() if password == correct_password: print("Great secrets lie here!")
Looks pretty good. Now Brian can neither look over your shoulder or at your code. Given that he's been thwarted, he's decided to turn away from a life of hacking to start his lifelong dream: his own cooking show.
I really hope you learned something today, and my greatest apologies for the inordinate length of the post!
Small update: Thank you all for the feedback and support for this article! This is the first learn post I've ever made. Brian got picked up by Netflix, and his cooking show is really taking off! Another update: Unfortunately, Joe Exotic overtook him
Wow! So cool...
Am I the only one whose first idea of testing a password is comparing two salted double-hashes (md5 of an sha256 and an sha256 of an md5)? By the way, there are tools that can be used to crack a hash (you do not decode a hash, you crack it using brute force). However, salted hashes are more difficult (I tried with a hash salted with brackets and the specific tool could not crack the hash, even with a good wordlist) and double-hashes are nearly impossible to crack based on what I know currently.
@AmazingMech2418 I think salting and double hashes are beyond the scope of this tutorial. If you need aerograde security, then you would implement it using them. It leads to more complexity. However, I think I will add an update to this tutorial about salting hashes, I just have a bit of stuff going on right now. Happy coding!
How would you decode a password from being a hash? I am trying to make a game with a login system and I want to hash the password which is inputted, then write it to a file, which I can access later to decode and open the users file.
what about if somebody looks at the script, copies the hash, runs it through the dehasher thing and then gets the password? is there any solution for that? if not, thats okay because this is the best solution i would think
This was a really helpful tutorial since I am making an RPG game with python myself!
@LelandJacobi Oh! You want to check your password! Here is some sample code to do that, assuming your password hash is assigned to a variable called
correct_password, and you've
# Sorry, I made a mistake here. I fixed it. while True: password = getpass(prompt="Enter password: ") if sha256(password.encode()).hexdigest() == correct_password: print("Correct password! Many secrets") break else: print("Wrong password, try again!")
Maybe Brian could simply use the SHA256 hash and just decode the password. (I'm not sure if that's possible, but I think it is.) What solutions would there be to that?
You could use prompt_toolkit, in which you can do this:
from prompt_toolkit import prompt password=prompt("Enter in your password", password=True)
It will be like a HTML password form input, replacing char's with *
@TaylorLiang You certainly can, or use some
sys.stdin magic to write that function yourself! The reason I didn't include this is in the tutorial is that external dependencies greatly slow down repl.it programs on wake-up, so I try to use them selectively, and I didn't want to add more confusion with a custom function.