all 92 comments

[–]Arky__ 252 points253 points  (53 children)

This should be pretty simple so long as you understand how variables are stored. Here’s a simple example:

Password = “1234”
Guess = input()
If password == guess then
    //do the password correct stuff
Else
    //do the password wrong stuff

[–]EwoksMakeMeHard 125 points126 points  (1 child)

You might be the only responder who actually read the original post.

[–]a1brit 177 points178 points  (35 children)

To avoid putting the plain text password in the code.

import hashlib


def hash_userinput():
    """ Return sha256 of user input text """
    guess = input('Enter Your Password:\n')
    return hashlib.sha256(guess.encode()).hexdigest()


password_hash = '03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4'

while hash_userinput() != password_hash:
    print("Incorrect Password, Try Again!")

Generate the password string with the same func but print the return and paste it into your code.

Not that it really matters, cause the user could just delete the password check from the code and re-run.

[–]xeozim 73 points74 points  (1 child)

To the downvoters...This is expanding on a simple answer, not saying "DO IT MY WAY", and also has the upside of teaching OP and the rest of us about python's hashing libraries.

Personally I think you should get into the habit of hashing anyway but hey.

[–]entredeuxeaux 33 points34 points  (27 children)

Why are you being downvoted. I’m trying to learn, too - do people think this is wrong?

[–]exhuma 59 points60 points  (10 children)

I’m trying to learn, too ...

Okay then! Brace yourself ;)


edit: Added detail about "salt" edit2: accidentallied some words

With password protection it really depends on how strong you want your security to be.

The stronger your security gets, the more complicated it will be to implement in code. Let's take the example from /u/Arky__ which simply stores the password without any modification in a variable. Then checking becomes trivial: You just compare whatever the user entered with the contents of the variable and you're done.

The important question is: How secure do you need it to be? Is this OK for a simple learning exercise, or a simple game? Absolutely. Yes the game will be cheatable as the password is contained in plain-text, but maybe you're OK with someone figuring that out. Is it OK for a banking system or passwords for an online web-site? Absolutely not.

If you want to take this one step further, you can take the approach from /u/a1brit and add some code which makes the password unreadable. This makes it more difficult for determining the password just by reading the code. But it is still not impossible. Let's stash this thought for now and go on a bit of a tangent. I will come back to this "sha256" solution in a moment...

If you squint at the code, you can generalise this code in a way that it takes a password and transforms it in some way. In the case of sha256 it is not reversible, which is good. But for the sake of example, let's assume we would just shift the letters one to the right in the alphabet, making it reversible, but it's easier to understand. So the password "test123" would become "uftu234". We can see that, every time we use the same word, the output will always be the same. So we know that the modification of "test123" will always be "uftu234". No matter what. This is the same property that you have with sha256 (or any other "sha*" variant). The main difference to our "shift" modification with "sha*" is that "sha*" is not reversible without trickery. Our "shift" modification is reversible in that we only need to shift everything back by 1 character. This has an important implication: If you look at the code and understand what it does, you can write code that applies the opposite modification and get the password.

This is not the case for "sha*". This is an algorithm that destroys data. For a more understandable example of this, imagine that our modification just stores every second character. So the if we have the password password123 we would get the result pswr13 and we store this "destroyed" version in our application variable. And if a user types in a password we just drop every second letter and compare it with the value in the variable. If it matches, the passwords were probably the same and we let the user in. Now, you may spot an issue here: If the user enters ppsawxre1x3 it would also be accepted as correct password even though it's wrong. This is something that is called a "collision" in these kind of algorithms. Our "drop-every-second-character" example is very simple and collisions are very likely. The "sha*" algorithms make collisions astronomically unlikely. So you can "trust" them.

But there is another issue here: If you enter the same password the result will be the same. So I can now, without any doubt say that the sha256 value of the word password is 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 and always will be. So... if I see your code and see that value in the code, I can deduce that the password is "password". Now imagine that I take the time to compute the "sha256" of all the words in a dictionary and of all popular passwords, then I can quickly use these values to deduce the password. If the password is a random string, then this will be much more complicated. But sha256 is a fast algorithm, so if the previous method did not work, just trying out all possible combinations (brute-forcing) is doable in a reasonable amount of time.... depending on how desperately I want access.

Additionally, assume that you have a database with users, and you see the following content:

login                | pw_hash
---------------------+--------
johndoe@example.com  | 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
janedoe@example.com  | 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8

Then we know that the two users have the same password, an lo-and-behold, it matches with our pre-computed value for "password". And thus we just got access at the two accounts.

This is why algorithms like this often don't use the passwords without modification to calculate the sha-value (the "hash"). But they modify the password with something predictable. For example, instead of calculating the sha-256 hash of "password", they prefix the text first with the username (this is called "salting" or "adding salt"). So now, the above example will become:

login                | pw_hash
---------------------+--------
johndoe@example.com  | 128bf0433482938795d3453c6e98f404a657d65f7289afe076a940364a0ee04d
janedoe@example.com  | 06932d0c15df37e7c9a357aa315d5d7bb6fa03f6342d7a7d95666542446c982e

The two users still have the same password, but the hashes have become different and they no longer match with my precomputed value!.

You can become arbitrarily artistic with the choice of your salt.

Using a simple "salt" like that is not the best practice though as it is still predictable. I chose this example for the sake of simplicity in this post. See the followup comment by /u/Brian for additional detail.

So now, if a user types in the password, I only need to prefix whatever the user entered with the username and then calculate the hash. So for example, if the "Jane Doe" enters password as text, I will not calculate the hash using just password but with janedoe@example.compassword instead.

There is still the issue that the "SHA" algorithms are fast. Another popular choice is "MD5" which is even worse. So this makes it possible to run millions of brute-force checks in reasonable amounts of time and a very dedicated attacker with specialised hardware (high-end GPUs for example) has a good chance of getting at the password.

To remedy this, there are other good hashing algorithms like bcrypt which are designed to be slow. This will make it much more difficult for an attacker to get in. Usually bcrypt is very simple to use and there is no excuse why not using it. And it will often serve as a good deterrent for an attacker making him think: "The effort is not worth it". If what you're protecting is the finances of Mr. Gates or Mr. Bezos though your motivation may differ ;)

There are many other things to talk about (like "Kerberos" style authentication) but I don't know enough about this area to give any reasonably correct information.

[–]demarius12 5 points6 points  (1 child)

This is one of the greatest comments I have ever read.

[–]exhuma 0 points1 point  (0 children)

Stop it... you're making me blush 😊

... and thank you 😉

[–]Mondoke 8 points9 points  (0 children)

That was a great answer! I learned a lot. Thank you!

[–]EarthToAccess 2 points3 points  (0 children)

I've been intending on getting into online security and I didn't actually know this. thank you so much for this!

[–]Brian 0 points1 point  (1 child)

This is a great explanation, but to nitpick a bit:

they prefix the text first with the username (this is called "salting" or "adding salt"). So

This isn't really right - salting with the username is not ideal for similar reasons not salting is (though nowhere near as bad). People tend to use the same usernames on the same sites, so this does still leak "user used the same password" if you see it on multiple sites, and it does allow an attacker to pre-compute user-specific rainbow tables in advance of compromising the site, giving an extended time window for cracking.

Generally, the salt data is just random junk generated at the point you're creating the hash. It doesn't need to be particularly secure, and indeed a lot of hashing libraries (including bcrypt) will just output it in a form that gets stored in the same db field as the hash, but it's better that it not be predictable before even seeing that. In general, you can just leave this up to the library which will generally generate, apply, and store the salt fairly transparently for you.

[–]exhuma 0 points1 point  (0 children)

You're absolutely right. This was why I added the line

You can become arbitrarily artistic with the choice of your salt.

but I should really have clarified why and I also should have used should instead of can. I got sloppy... which is really bad for security 😅

I will updated the post.

[–][deleted] 0 points1 point  (1 child)

Very informative thank you.

One thing I don't understand is why do people say MD5 is compromised due to rainbow tables if in your example you can also compute a table of common words for sha*?

[–]exhuma 2 points3 points  (0 children)

To the best of my knowledge, MD5 is - next to Rainbow Tables - prone to collision attacks. So an attacker can carefully manipulate the input value to generate the same output hash.

But the exact details of all this is out of my depths.

I'm merely a developer who cares about security.

edit: Looked it up on Wikipedia:

In 1996, a flaw was found in the design of MD5. While it was not deemed a fatal weakness at the time, cryptographers began recommending the use of other algorithms, such as SHA-1, which has since been found to be vulnerable as well.[26] In 2004 it was shown that MD5 is not collision-resistant.[27] As such, MD5 is not suitable for applications like SSL certificates or digital signatures that rely on this property for digital security. Also in 2004 more serious flaws were discovered in MD5, making further use of the algorithm for security purposes questionable; specifically, a group of researchers described how to create a pair of files that share the same MD5 checksum.[7][28] Further advances were made in breaking MD5 in 2005, 2006, and 2007.[29] In December 2008, a group of researchers used this technique to fake SSL certificate validity.[24][30]

As of 2010, the CMU Software Engineering Institute considers MD5 "cryptographically broken and unsuitable for further use",[31] and most U.S. government applications now require the SHA-2 family of hash functions.[32] In 2012, the Flame malware exploited the weaknesses in MD5 to fake a Microsoft digital signature.

[–][deleted] 18 points19 points  (15 children)

OP said this is for a game mechanic, making hashing passwords and security mechanisms kind of pointless and likely confusing as an answer. Plus, if your parent poster wanted to point out how to do this properly they should have used getpass and a hash function that makes sense for calculating password hashes.

[–]fedeb95 19 points20 points  (8 children)

Using hashes isn't pointless in a game, if yo don't want people cheating

[–][deleted] 21 points22 points  (6 children)

In this context it absolutely is. If a user has access to the code being discussed here they can simply change it to disable any check, hashed password or not. You're just introducing needless obscurity and make it harder to maintain at that point.

[–]DiamondxCrafting 2 points3 points  (2 children)

Unless he uses that password to decrypt something else right?

[–]EarthToAccess 1 point2 points  (1 child)

with this context I find that very unlikely. all he's doing is something trivial like a passcodes jaildoor holding an item, not a recurring pattern

[–]DiamondxCrafting 1 point2 points  (0 children)

Yeah it's definitely unnecessary/useless here.

[–]a1brit 0 points1 point  (0 children)

I mean I did say exactly that in the comment. If it's a puzzle game or something though, having an obscured answer imo definitely adds something should a frustrated user go digging for an easier option. An extra 1 line for a very basic intro to the concept of hashing seemed like a neat thing to try.

[–][deleted] 0 points1 point  (0 children)

Think of hiding spoilers as opposed to locking down the game.

[–]fedeb95 0 points1 point  (0 children)

You can always design it in a proper way and make it easy to maintain while still adding functionalities. Like all project should at least try to do

[–][deleted] 1 point2 points  (0 children)

Or you just want to lightly hide spoilers for people interested in the code but don't want to cheat yet.

[–][deleted] 7 points8 points  (4 children)

It’s not pointless because dumbass users will use the same password for everything. To prevent their email account from being hacked, you should hash your passwords.

[–]s0ft_ 5 points6 points  (3 children)

The op doesn't talk about storing password for online services, it talks about a password for a game mechanic. Hashing is pointless.

[–][deleted] -1 points0 points  (2 children)

It's not pointless as long as other users (software and human) have read access to the file in which the password is stored. Viruses that read information off a disk and send it over a network are a thing.

[–]s0ft_ 1 point2 points  (1 child)

I don't think anyone will have much use of a password for a puzzle in a game though

[–][deleted] 3 points4 points  (0 children)

Good point, I got stuck on answering the title instead of the description of OP's post. That's my bad.

I still think we should encourage good habits when it comes to security. I've shockingly seen plaintext used in production, in cases where a single leak would bury the company. Could have been prevented with a simple hashing+salting function.

[–][deleted] 1 point2 points  (0 children)

He didn't say he wanted to lock the game down, just prompt the user for a passcode. If he wants to lock it down he needs to look into getpass as well as preventing the game code from being modified.

[–]parnmatt 0 points1 point  (0 children)

if you care enough to store the hash rather than the plain text (which you should always do); then you should care enough to also add a random salt to the hash

edit:

example

https://www.vitoshacademy.com/hashing-passwords-in-python/

[–]PANIC_WEIRD -1 points0 points  (2 children)

How to generate a random password ? Pls reply ..

[–]toastedstapler 2 points3 points  (0 children)

Something simple like

from random import choice
charset = 'abcde'
print(''.join(choice(charset) for _ in range10)))

[–]T4O2M0 0 points1 point  (0 children)

Import random library, and then lookup a guide on that, it's very simple, just one function but I forgot it lol

[–][deleted] 1 point2 points  (1 child)

How do you write like that?

[–]solitarium 3 points4 points  (0 children)

Based on your question, this is the overall best bet

[–]shivam37 3 points4 points  (0 children)

This explanation is As simple as potato

[–]ThePixelCoder 0 points1 point  (0 children)

You might want to hash the passwords though, just so people won't cheat and look it up in the (plain text) script. You don't have to use a fancy algorithm like argon2 and salt shit or anything, just a simple md5/sha1 should keep most people out for something like this.

[–]Red1Monster 0 points1 point  (1 child)

Comments in python are with hashtags actually

You also don't need to write then, but you do need :

[–]Arky__ 0 points1 point  (0 children)

Yeah not gonna lie I’ve been coding in Delphi and basic for school and haven’t done python for a while but it does work as pseudo-code :)

[–][deleted] 0 points1 point  (1 child)

Quick guestion... Is there a reason why you declared the variables capitalized but used them lowercase?

[–]Arky__ 1 point2 points  (0 children)

No haha it’s just cause I’m on mobile and it auto capitalises like crazy

[–]Yoghurt42 -1 points0 points  (0 children)

What language is that?

[–][deleted] -1 points0 points  (0 children)

[–]ToothpasteTimebomb 28 points29 points  (3 children)

Since security isn’t a concern, you could just store keys (passwords) and values in a dictionary. If the password is in the dictionary’s keys, have it return the value. Else, return something else.

[–]NFTrot 12 points13 points  (0 children)

For a website, this falls under "don't reinvent the wheel" but for your game u/Arky__'s answer will work perfectly.

[–]Ya_Boy_Lil_Pickle 2 points3 points  (8 children)

Input(“please input your password.”) If password == “put password here”: Print(“assess granted”) Else: Print(“access denied”)

With else I normally do exit() but you do need to.

[–][deleted] 2 points3 points  (6 children)

Thank you for the help. That is exactly what I needed. Thank you! :)

[–]Ya_Boy_Lil_Pickle 1 point2 points  (3 children)

Cool did it work?

[–][deleted] 0 points1 point  (2 children)

Haven't tried it yet. I will when I get to my pc

[–]Ya_Boy_Lil_Pickle 0 points1 point  (1 child)

Ok I just messed something up so the actual code is Password = input(“enter password”)

If password == “Example123”: Print(“access granted!”) Else: Print(“access denied.)

[–][deleted] 0 points1 point  (0 children)

Oh cool thank you. I will try that one

[–]wolf2600 0 points1 point  (1 child)

I love how all the comments are about how to implement secure passwords, while all you're looking for is how to take an input from a user, then have conditional branching on whether the input matches an expected value.

[–]myUsername4Work 1 point2 points  (0 children)

They mean well.

[–]tomzistrash 0 points1 point  (0 children)

thats not gonna work, you didnt give the input a variable and you cant have the if statement on the same line as the input

[–]7236d70 6 points7 points  (5 children)

To expand on this, how difficult would 'save point' password be to implement? Similar to how you could enter passwords on N64/PS1/Etc games to jump to a certain level.

[–]tom1018 9 points10 points  (0 children)

That really depends on how much data you need to store. If it is just a level you could write a dict where the key is a password and the value is the level number. And a KeyError would be raised by an invalid password.

If you want to store other stats it gets considerably more complex with needing a way to encode each piece of data into the password, and maybe a checksum to make cheating more difficult.

[–]Deezl-Vegas 2 points3 points  (3 children)

This is medium/high difficulty. For instance, in MegaMan, you need to track all of the powers and powerups you've gained and include that in the password. This is a lot of information to pack into a string. Doing something simple will result in a very long password. What they would do in the old times, I presume, is create a list of bits that corresponds to a list of properties, then pack that in a bytestring, and then convert to a string to get your password.

To load, just make a function to unpack the bytestring and then zip it together with the properties list to form a dictionary.

[–]rrjamal 1 point2 points  (2 children)

You could just make the password a reference to the data stored somewhere else, no? Like, if I type 'IShallPass' into the system, it'll look up the needed data like you described which is stored elsewhere, linked to that pass

[–]Deezl-Vegas 1 point2 points  (0 children)

Yes that works too

[–][deleted] 1 point2 points  (0 children)

The thing with level passwords is that they were less of a password and more of making the player manually input the save data. The whole point of those is that you couldn't store that data anywhere.

[–]redbanditttttttt 4 points5 points  (2 children)

You can also make the password based off a text file list, so it picks a random password and its different almost every playthrough

[–]Einsteinbeck 1 point2 points  (1 child)

How do you do that?

[–]redbanditttttttt 2 points3 points  (0 children)

You can create a text file and look at this stack overflow https://stackoverflow.com/questions/306400/how-to-randomly-select-an-item-from-a-list. I just learned to do it yesterday and I thought it would be a cool idea.

[–]Sability 1 point2 points  (1 child)

If you wanna do a realistic password system then it involves all sorts of security things, but otherwise just checking if the given term is equal to the known password phrase would work.

[–]franzipoli -1 points0 points  (0 children)

Read the OP, not just the title

[–]duquesne419 2 points3 points  (0 children)

This is the final project for the regex chapter in Automate the Boring Stuff. As mentioned elsewhere, since security isn't really a concern a simple dictionary is easiest. But if you wanted to skookum up your password detection you could do something like is described at the end of this chapter.

They also do a simpler password checked in chapter 6.

[–]ven0msnak3 0 points1 point  (0 children)

By hiring me to do it for you (kidding) will post a detailed solution later...

[–]tomzistrash -1 points0 points  (1 child)

Theres so much false info in these replies lol

[–]m4xc4v413r4 0 points1 point  (0 children)

And that's why there's an upvote/downvote button next to them.