Learn to Code via Tutorials on Repl.it!

← Back to all posts
How to do Passwords Right
h
NotTani (72)

Hello repl.it!

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.

What-shes?

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 .hexdigest().

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!
Happy coding!

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

Commentshotnewtop
Codemonkey51 (871)

Cool tutorial! I've messed around with this stuff before and made a python password module it's really fun to do this tutorial would have really helped me when I was making that module.

AmazingMech2418 (909)

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.

NotTani (72)

@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!

Viper2211 (82)

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.

CodeLongAndPros (1377)

@Viper2211, hashes are one-way, at least MD5. The way to do what you are describing would be a dict of hashed passwords and users associated with their account. You would read in the hashed password, use a for loop, and if the hash is in the dict, get the users name.

Viper2211 (82)

Thanks @CodeLongAndPros ! I got it now! Thanks for clarifying!

21natzil (1136)

Awesome! Maybe you can make a followup tutorial where you salt your hashes.

NotTani (72)

@21natzil That's a good idea, I think I will, I just have a quite bit of stuff going on.

mikepython (10)

How does one make a savesystem?

SixBeeps (3081)

That getpass module looks very handy. Great post!

imacceol (3)

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

NotTani (72)

@imacceol Dehashing requires a decent amount of computing power and a long time. No algorithm is 100% secure, because there is no way to simple way to prevent brute forcing.

AmazingMech2418 (909)

@NotTani Not with the right tools, it isn't! Cracking hashes can be easy with a specific Linux tool (not saying the name since, while I use it for ethical reasons (i.e. pen-testing my own systems), other people might not), given that they aren't salted or double-hashes.

LelandJacobi (0)

This was a really helpful tutorial since I am making an RPG game with python myself!

NotTani (72)

@LelandJacobi Glad I could be helpful! Good luck!

LelandJacobi (0)

@NotTani I was testing my RPG with the hashed password but it wouldn't let me type in the password. Is there a reason for this, if so how do I fix it?

NotTani (72)

@LelandJacobi I'd be happy to help... can I see your code? EDIT: I found your RPG on your profile. You need to replace the correct_password with a generated password hash (generator above). With getpass, you won't be able to see the password as you type it.

LelandJacobi (0)

@NotTani I tried that but I could just press enter and it would move on to the story.

NotTani (72)

@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 imported sha256 and getpass:

# 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!")
MatthewDoan1 (327)

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?

NotTani (72)

@MatthewDoan1 That is a good point! However, cryptographic hashes (such as SHA256) are specifically designed to be irreversible: that's what makes them secure.

MatthewDoan1 (327)

@drwhonerd99 Oh, thanks. I've used other things (I don't know if they're cryptographic hashes or not) like MD5, and those can be reversed, so I thought the same applied to SHA-256.

NotTani (72)

@MatthewDoan1 Well, theoretically, any hashing algorithm can be brute forced by guessing until you get it right, but decoding an SHA256 hash is infeasible as long as you have a pretty good password.

MatthewDoan1 (327)

@drwhonerd99 That is true. I'm thinking about this method of cracking a password where the hacker keeps sending in passwords and measures the response time from the server. Do you know what that's called? I'm completely blanking.

NotTani (72)

@MatthewDoan1 It's called a timing attack. It can be dangerous, but in this specific case, the correct password hash is not calculated during the program, so there isn't a danger of a timing attack here.

MatthewDoan1 (327)

@NotTani Yes, thanks for the answer. And I know that the correct password hash is not calculated during the program, but in a different application, it could be. So I just wanted to know the name and research it.

Highwayman (1358)

I thought sha256 wasn’t safe..?

NotTani (72)

@Highwayman You're right! SHA256 is not the securest algorithm on the planet. However, for the purposes of this tutorial, I chose something that had a simple interface, decently secure, and pretty fast. I added a notice to the article.

Highwayman (1358)

Happily reading along an interesting tutorial

...or Assembly

Wat.

NotTani (72)

@Highwayman

section	.text
   global_start  
	
_start:	          
   mov	edx,len  
   mov	ecx,msg  
   mov	ebx,1    
   mov	eax,4    
   int	0x80    
   mov	eax,1    
   int	0x80
section	.data
msg db "What could be bad about Assembly?", 0xa 
len equ $ - msg
Highwayman (1358)

@drwhonerd99 :/ XD I’m actually trying to learn a little bit of assembly rn, I’m still kinda stuck on the first chapter of my book though :( it has these tests at the end of the chapters and I just fail half of the questions even though it’s like my fifth time reading the chapter.

NotTani (72)

@Highwayman TBH, I just found an Assembly Hello World. It's a really hard language. Good luck, though!

[deleted]

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 *

NotTani (72)

@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.

LiamDonohue (285)

this is an excellent tutorial! upvote

NotTani (72)

@LiamDonohue I appreciate that! Means a lot!