Le code utilisé ici est sur github. https://github.com/matsulib/bcrypt-dictionary-attack
table des matières
Si vous souhaitez utiliser la fonction de hachage bcrypt adaptée au stockage de mots de passe depuis Python, utilisez le module bcrypt (https://pypi.python.org/pypi/bcrypt/3.1.1).
** Méthode d'installation **:
** Caractéristiques de bcrypt **
bcrypt est une fonction de hachage basée sur la cryptographie Blowfish.
La différence décisive entre bcrypt et les fonctions de hachage telles que les familles MD5 et SHA est que la première est un hachage rapide, tandis que la seconde est un hachage lent.
Le hachage rapide étant rapide, il est pratique d'obtenir un condensé de longueur fixe à partir d'un fichier volumineux, mais si vous l'utilisez pour la gestion des mots de passe, il existe un problème en raison du fait que si la valeur de hachage est divulguée, elle est facilement craquée par une attaque hors ligne. .. Par conséquent, il existe une technique appelée étirement (répétition du hachage) qui ralentit intentionnellement le processus.
Bcrypt, en revanche, est un hachage lent conçu à l'origine pour itérer, et non seulement lent, mais également conçu pour rendre difficile la mise en œuvre matérielle rapide.
This system hashes passwords using a version of Bruce Schneier's Blowfish block cipher with modifications designed to raise the cost of off-line password cracking and frustrate fast hardware implementation.
Basically, computational power can be parallelized cheaply and easily, but memory cannot. This is the cornerstone of bcrypt and scrypt. Obviously, they can still be broken by sheer brute force, and you could just use hardware with integrated memory units to circumvent the problem, but it's much harder and much, much more expensive to do so.
Pour être honnête, je ne sais pas si l'explication ci-dessus est correcte ou toujours valable en raison d'un manque de compréhension.
Il semble que bcrypt a également été utilisé dans l'incident de fuite d'informations sur le titulaire d'Ashley Madison survenu en juillet 2015. Cependant, il semble que ce soit MD5 (et on savait également qu'il s'agissait de 26 lettres de l'alphabet inférieur) qui était fatal, et il semble que bcrypt lui-même soit encore une existence douloureuse pour l'attaquant.
Instead of cracking the slow bcrypt hashes directly, which is the hot topic at the moment, we took a more efficient approach and simply attacked the md5(lc($username).”::”.lc($pass)) and md5(lc($username).”::”.lc($pass).”:”.lc($email).”:73@^bhhs&#@&^@8@*$”) tokens instead. Having cracked the token, we simply then had to case correct it against its bcrypt counterpart.
** Utilisation de base de bcrypt **
--bcrypt.gensalt (rounds = 12, prefix = b'2b ') # Générer du sel --bcrypt.hashpw (mot de passe, sel) # Mot de passe de hachage --bcrypt.checkpw (password, hashed_password) #verify password
Hachez le mot de passe comme suit:
python
>>> import bcrypt
>>> salt = bcrypt.gensalt(rounds=10, prefix=b'2a')
>>> salt
b'$2a$10$VpsqBArIfdhGzJY1YO/xyO'
>>> password = b'password'
>>> bcrypt.hashpw(password, salt)
b'$2a$10$VpsqBArIfdhGzJY1YO/xyOiOsCrQc9BZAaonPt3QDsL0HWzoaHXgG'
Il a la structure suivante.
$(Version à 2 caractères)$(Œuvre à 2 caractères-factor)$(22 caractères de sel)(Hachage de 31 caractères)
** À propos de l'argument de bcrypt.gensalt () **
bcrypt.gensalt () générera un sel différent pour chaque appel. Vous pouvez également spécifier deux arguments lors de l'appel.
Actuellement, "2a" semble être le courant dominant.
Ci-dessous, nous examinerons le temps de calcul lorsque la valeur du facteur de travail de bcrypt.gensalt () est modifiée.
** Environnement d'exécution: **
** Code et résultats **
python
In [1]: import bcrypt
In [2]: password = b'password'
In [3]: for i in range(5, 18):
...: print('rounds: {:02d}'.format(i), end=' => ')
...: %timeit -r1 bcrypt.hashpw(password, bcrypt.gensalt(rounds=i))
...:
rounds: 05 => 100 loops, best of 1: 2.73 ms per loop
rounds: 06 => 100 loops, best of 1: 5.4 ms per loop
rounds: 07 => 100 loops, best of 1: 10.7 ms per loop
rounds: 08 => 10 loops, best of 1: 21 ms per loop
rounds: 09 => 10 loops, best of 1: 42.1 ms per loop
rounds: 10 => 10 loops, best of 1: 84.4 ms per loop
rounds: 11 => 10 loops, best of 1: 172 ms per loop
rounds: 12 => 1 loop, best of 1: 369 ms per loop
rounds: 13 => 1 loop, best of 1: 703 ms per loop
rounds: 14 => 1 loop, best of 1: 1.4 s per loop
rounds: 15 => 1 loop, best of 1: 2.71 s per loop
rounds: 16 => 1 loop, best of 1: 5.42 s per loop
rounds: 17 => 1 loop, best of 1: 10.8 s per loop
Il n'est pas surprenant que chaque incrément de facteur travail double le temps de calcul.
** Environnement d'exécution **
** Code SHA256 (+ sel) **
hashing_sha256.py
import uuid
import hashlib
# Reference: http://pythoncentral.io/hashing-strings-with-python/
def hash_password(password):
# uuid is used to generate a random number
salt = uuid.uuid4().hex
return hashlib.sha256(salt.encode() + password.encode()).hexdigest() + ':' + salt
def check_password(hashed_password, user_password):
password, salt = hashed_password.split(':')
return password == hashlib.sha256(salt.encode() + user_password.encode()).hexdigest()
if __name__ == '__main__':
new_pass = input('Please enter a password: ')
hashed_password = hash_password(new_pass)
print('The string to store in the db is: ' + hashed_password)
old_pass = input('Now please enter the password again to check: ')
if check_password(hashed_password, old_pass):
print('You entered the right password')
else:
print('I am sorry but the password does not match')
Résultat d'exécution
> python hashing_sha256.py
Please enter a password: piyo
The string to store in the db is: 25bcf883839511cdb493b3ba25bc0ad3fc809a3688076d198ef13128f5883a66:f0ac11a13a314336951392ff52c9c2d6
Now please enter the password again to check: piyo
You entered the right password
** code bcrypt ** Le résultat de l'exécution est le même que le code SHA256.
hashing_bcrypt.py
import bcrypt
def hash_password(password, rounds=12):
return bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds)).decode()
def check_password(hashed_password, user_password):
return bcrypt.checkpw(user_password.encode(), hashed_password.encode())
** Code d'attaque du dictionnaire **
dictionary_attack.py
import time
import hashing_sha256 as sha256
import hashing_bcrypt as bcrypt
def attack(leaked_hashed_password, hashing):
# https://www.teamsid.com/worst-passwords-2015/
dictionary = ['123456', 'password', '12345678', 'qwerty', '12345',
'123456789', 'football', '1234', '1234567', 'baseball',
'welcome', '1234567890', 'abc123', '111111', '1qaz2wsx',
'dragon', 'master', 'monkey', 'letmein', 'login', 'princess',
'qwertyuiop', 'solo', 'passw0rd', 'starwars']
for p in dictionary:
if hashing.check_password(leaked_hashed_password, p):
return 'Frappé! Le mot de passe est{}est.'.format(p)
else:
return 'De(´ ・ ω ・ `)'
passwords = ['complex.password'] * 9 + ['passw0rd']
print('sha256')
leaked_sha256 = [sha256.hash_password(p) for p in passwords]
st = time.time()
for i, v in enumerate(leaked_sha256):
result = attack(v, sha256)
print('user{:02d}: {}'.format(i, result))
print('Total time: {:.3f} s'.format(time.time()-st))
print('bcrypt-5')
leaked_bcrypt = [bcrypt.hash_password(p, 5) for p in passwords]
st = time.time()
for i, v in enumerate(leaked_bcrypt):
result = attack(v, bcrypt)
#print('user{:02d}: {}'.format(i, result))
print('Total time: {:.3f} s'.format(time.time()-st))
print('bcrypt-12')
leaked_bcrypt = [bcrypt.hash_password(p) for p in passwords]
st = time.time()
for i, v in enumerate(leaked_bcrypt):
result = attack(v, bcrypt)
#print('user{:02d}: {}'.format(i, result))
print('Total time: {:.3f} s'.format(time.time()-st))
résultat
sha256
user00:De(´ ・ ω ・ `)
user01:De(´ ・ ω ・ `)
user02:De(´ ・ ω ・ `)
user03:De(´ ・ ω ・ `)
user04:De(´ ・ ω ・ `)
user05:De(´ ・ ω ・ `)
user06:De(´ ・ ω ・ `)
user07:De(´ ・ ω ・ `)
user08:De(´ ・ ω ・ `)
user09:Frappé! Le mot de passe est passw0rd.
Total time: 0.005 s
bcrypt-5
Total time: 0.715 s
bcrypt-12
Total time: 90.040 s
Vous pouvez voir que les attaques par dictionnaire sur bcrypt (et bien sûr les attaques Brute Force) prennent beaucoup plus de temps que SHA256. Le nombre réel de dictionnaires et d'utilisateurs est plus grand, il devrait donc être efficace pour harceler les attaquants.
** 1. Lorsque vous utilisez bcrypt depuis Python, utilisez le module bcrypt **
** 2. Choisissez le bon facteur de travail **
En définissant une valeur élevée pour le facteur de travail, il est possible de gagner du temps pour l'attaque hors ligne d'un attaquant, mais cela affecte également les performances pendant le fonctionnement normal. C'est un compromis entre sécurité et commodité.
Déterminez le nombre d'itérations que le serveur vérifiera le mot de passe dans le délai souhaité (10, 200ms, etc.) et l'utilisera.
I don’t believe that there is a “correct” work factor; it depends on how strong you want your hashes to be and how much computational power you want to reserve for the hashing process.
fin.
Recommended Posts