Hachez lentement les mots de passe en utilisant bcrypt en Python

Le code utilisé ici est sur github. https://github.com/matsulib/bcrypt-dictionary-attack

table des matières

introduction

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.

  1. tours: un paramètre appelé facteur de travail pour ralentir le calcul des valeurs de hachage, qui peut être spécifié de 4 à 31 (par défaut = 12). L'opération est répétée 2 ^ tours.
  2. préfixe: vous pouvez spécifier b'2a'ou b'2b '(par défaut = b'2b').

Actuellement, "2a" semble être le courant dominant.

Expérience 1. Effet du facteur travail

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.

Expérience 2. Cela ressemble à une attaque par dictionnaire (comparaison avec SHA256)

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

Résumé

** 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

Hachez lentement les mots de passe en utilisant bcrypt en Python
Traduit à l'aide de googletrans en Python
Utilisation du mode Python dans le traitement
Programmation GUI en Python avec Appjar
Essayez d'utiliser LevelDB avec Python (plyvel)
Utilisation de variables globales dans les fonctions python
Voyons voir l'utilisation de l'entrée en python
Puissance totale en Python (en utilisant functools)
Méthode Hash (méthode d'adresse ouverte) en Python
Reconnaissance de caractères manuscrits à l'aide de KNN en Python
Essayez d'utiliser LeapMotion avec Python
Recherche de priorité de profondeur à l'aide de la pile en Python
Lors de l'utilisation d'expressions régulières en Python
Création d'interface graphique en python avec tkinter 2
Fonctionnement de la souris à l'aide de l'API Windows en Python
Notes utilisant cChardet et python3-chardet dans Python 3.3.1.
Création d'interface graphique en python à l'aide de tkinter partie 1
Pratique d'utilisation de ceci en Python (mauvais)
Essayez d'utiliser l'API Kraken avec Python
Utilisation de venv dans un environnement Windows + Docker [Python]
[FX] Hit oanda-API avec Python en utilisant Docker
Tweet à l'aide de l'API Twitter en Python
[Python] [Windows] Communication série en Python à l'aide de DLL
J'ai essayé d'utiliser l'optimisation bayésienne de Python
Connectez-vous à Slack à l'aide de requêtes en Python
Obtenez des données Youtube en Python à l'aide de l'API Youtube Data
Hash en Perl est un dictionnaire en Python
Utilisation des constantes physiques dans Python scipy.constants ~ constant e ~
Scraping de sites Web à l'aide de JavaScript en Python
Développement de slack bot avec python en utilisant chat.postMessage
Ecrire un module python dans fortran en utilisant f2py
Dessinez une structure arborescente en Python 3 à l'aide de graphviz
Remarques sur l'utilisation de python (pydev) avec eclipse
Classification des maladies par Random Forest en utilisant Python
Téléchargez des fichiers dans n'importe quel format en utilisant Python
Exécution de tâches parallèles à l'aide de concurrent.futures en Python
Quadtree en Python --2
Python en optimisation
CURL en Python
Géocodage en python
SendKeys en Python
Créer un fichier GIF en utilisant Pillow en Python
Méta-analyse en Python
Pièces jointes par e-mail à l'aide de votre compte gmail avec python.
Unittest en Python
Création d'un processus de numérotation à l'aide de python dans le processus de numérotation locale DynamoDB
Essayez d'utiliser l'API BitFlyer Ligntning en Python
Dictionnaire [Python] (hachage)
Époque en Python
Discord en Python
Obtenir l'URL de l'image à l'aide de l'API Flickr en Python
Commencez à utiliser Python
Allemand en Python
DCI en Python
Remarques sur l'utilisation de dict avec python [Competition Pro]
tri rapide en python
nCr en python
Jugons les émotions à l'aide de l'API Emotion en Python