Hash Functions and Its Importance
Hash Functions and Its Importance
10
Like
10
103
Share
From time to time, servers and databases are stolen or compromised. With this in mind, it is important to ensure that
some crucial user data, such as passwords, can not be recovered. Today, we are going to learn the basics behind hashing
and what it takes to protect passwords in your web applications.
Disclaimer
Cryptology is a sufficiently complicated subject, and I am by no means an expert. There is constant research happening in
this area, in many universities and security agencies.
In this article, I will try to keep things as simple as possible, while presenting to you a reasonably secure method of storing
passwords in a web application.
1/10
5/18/2014
Encryption secures messages so that they can be verified as accurate and protected from interception. Pythons
cryptography support includes hashlib for generating signatures of message content using standard algorithms, such as
MD5 and SHA, and hmac for verifying that a message has not been altered in transmission.
A common example of a hash function is md5() , which is quite popular in many different languages and systems.
1 import hashlib
2
3 data = "Hello World"
4
5 h = hashlib.md5()
6 h.update(data)
7 print(h.hexdigest())
8 # b10a8db164e0754105b7a99be72e3fe5
To calculate the MD5 hash, or digest, for a block of data (here an ASCII string), first create the hash object, and then add the
data and call digest() or hexdigest(). This example uses the hexdigest() method instead of digest() because the output
is formatted so it can be printed clearly. If a binary digest value is acceptable, use digest().
Once we decide on a decent method for hashing the password, we are going to implement this process later in this article.
Note that the original password has never been stored anywhere. If the database is stolen, the user logins can not be
compromised, right? Well, the answer is it depends. Lets look at some potential problems.
2/10
5/18/2014
Now, lets assume the role of a person who has stolen a database, and has the hash value. We may not be able to convert
323322056
into supersecretpassword, however, we can figure out another password that will convert to the same hash
1 import binascii,base64
2
3 i=0
4 while True:
5
if binascii.crc32(base64.encodestring(bytes(i,))) == 323322056:
print(base64.encodestring(i))
i += 1
This may run for a while, though, eventually, it should return a string. We can use this returned string instead of
supersecretpassword
For example, after running this exact script for a few moments on my computer, I was given MTIxMjY5MTAwNg==. Lets test
it out:
1 import binascii
2
3 print(binascii.crc32("supersecretpassword"))
4 #323322056
5
6 print(binascii.crc32("MTIxMjY5MTAwNg=="))
7 #323322056
example,
md5()
http://pypix.com/python/hash-functions/
might
be
suitable,
as
it
generates
128-bit
hashes.
This
translates
into
3/10
5/18/2014
Sha1
Sha1() is a better alternative, and it generates an even longer 160-bit hash value.
What we basically do is concatenate the salt string with the passwords before hashing them. The resulting string
obviously will not be on any pre-built rainbow table. But, were still not safe just yet!
http://pypix.com/python/hash-functions/
4/10
5/18/2014
easypassword
as well. When they run all of the 10 million stolen salted hashes against this table, they will
This is assuming that a users id number never changes, which is typically the case.
We may also generate a random string for each user and use that as the unique salt. But we would need to ensure that we
store that in the user record somewhere.
1 import hashlib, os
2
3 def unique_salt():
4
return hashlib.sha1(os.urandom(10)).hexdigest()[:22]
5
6 salt = unique_salt()
7 password = "" # str or int
8 hash = hashlib.sha1(salt + str(password)).hexdigest()
9 print(hash)
10 # 37dec03d2761122819f8708e6d5c8392ee02b40d
This method protects us against Rainbow Tables, because now every single password has been salted with a different
value. The attacker would have to generate 10 million separate Rainbow Tables, which would be completely impractical.
5/10
5/18/2014
If the password can contain lowercase, uppercase letters and number, that is 62 (26+26+10) possible characters.
An 8 character long string has 62^8 possible versions. That is a little over 218 trillion.
At a rate of 1 billion hashes per second, that can be solved in about 60 hours.
And for 6 character long passwords, which is also quite common, it would take under 1 minute.
Feel free to require 9 or 10 character long passwords, however you might start annoying some of your users.
5
6
for i in range(1000):
7
8
hash = hashlib.sha1(hash).hexdigest()
return hash
9
10 print(my_hash("12345", "f#@V)Hu^%Hgfds"))
Or you may use an algorithm that supports a cost parameter, such as BLOWFISH . In Python, this can be done using the
py-crypt
library.
1 import bcrypt
2
3 def my_hash(password):
4
5
6 print(my_hash("atdk"))
7 #$2a$10$WNhGOdVhoZrrKgwxGa2VIuzfAvm9oFWZF9PIVtLIoU5LQOVGLuLrq
6/10
5/18/2014
5
6 def unique_salt():
7
return hashlib.sha1(os.urandom(10)).hexdigest()[:22]
8
9 password = "verysecret"
10
11 print(my_hash(password, unique_salt()))
12 # $2a$10$aHx0q.FE/tGvGWzlm6yePemYx9SAsBP2iSiy/uFx7pyjpy980Hita
The resulting hash contains the algorithm ($2a), the cost parameter ($10), and the 22 character salt that was used. The rest
of it is the calculated hash. Lets run a test:
1 import bcrypt, os, hashlib
2
3 # assume this was pulled from the database
4 hash = "$2a$10$6XDaX/3kNby0jI9Ih/Re7.478DOMZK9OnA2mTxKUP0My.39N.jdky"
5
6 # assume this is the password the user entered to log back in
7 password = "verysecret"
8
9 def check_password(hash, password):
10
salt = hash[:29]
11
12
13
14 if check_password(hash, password):
15
print("Access Granted")
16 else:
17
print("Access Denied")
Putting it Together
With all of the above in mind, lets write a utility class based on what we learned so far:
1 import bcrypt, os, hashlib
2
3 class PassHash():
4
5
def unique_salt(self):
return hashlib.sha1(os.urandom(10)).hexdigest()[:22]
6
7
8
9
10
http://pypix.com/python/hash-functions/
7/10
5/18/2014
11
full_salt = hash[:29]
12
13
14
15 obj = PassHash()
16
17 a = obj.hash("12345")
18 print(a) # $2a$10$gBSbmXKanQJOTSabtX4wfOE2RT2mKDFbCY6r7cqCJSk2YPGjIDrou
19
20 b = obj.check_password(a, "12345")
21 print(b) # True
Now, we can use this utility in our forms to hash the passwords securely.
Conclusion
This method of hashing passwords should be solid enough for most web applications. That said, dont forget: you can also
require that your members use stronger passwords, by enforcing minimum lengths, mixed characters, digits & special
characters.
A question to you, reader: how do you hash your passwords? Can you recommend any improvements over this
implementation?
Download article as PDF
http://pypix.com/python/hash-functions/
8/10
5/18/2014
ALSO ON PYPIX
3 Comments
pypix
Sort by Best
Login
Share
Favorite
4 months ago
Reply Share
aRkadeFR
2 months ago
laike9m
2 months ago
Subscribe
http://pypix.com/python/hash-functions/
9/10
5/18/2014
http://pypix.com/python/hash-functions/
10/10