Der hier verwendete Code ist auf Github. https://github.com/matsulib/bcrypt-dictionary-attack
Inhaltsverzeichnis
Wenn Sie die Hash-Funktion bcrypt verwenden möchten, die für die Kennwortspeicherung in Python geeignet ist, verwenden Sie das bcrypt-Modul (https://pypi.python.org/pypi/bcrypt/3.1.1).
** Installationsmethode **:
** Funktionen von bcrypt **
bcrypt ist eine kryptobasierte Blowfish-Hash-Funktion.
Der entscheidende Unterschied zwischen bcrypt- und Hash-Funktionen wie der MD5- und der SHA-Familie besteht darin, dass erstere ein schneller Hash ist, während letztere ein langsamer Hash ist.
Da schneller Hash schnell ist, ist es praktisch, einen Digest mit fester Länge aus einer großen Datei abzurufen. Wenn Sie ihn jedoch für die Kennwortverwaltung verwenden, besteht das Problem, dass der durchgesickerte Hash-Wert durch einen Offline-Angriff leicht geknackt werden kann. .. Daher gibt es eine Technik namens Dehnen (wiederholtes Hashing), die den Prozess absichtlich verlangsamt.
Bcrypt hingegen ist ein langsamer Hash, der ursprünglich für die Iteration entwickelt wurde und nicht nur langsam ist, sondern auch die schnelle Hardware-Implementierung erschwert.
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.
Um ehrlich zu sein, bin ich mir nicht sicher, ob die obige Erklärung korrekt oder aufgrund mangelnden Verständnisses noch gültig ist.
Es scheint, dass bcrypt auch in dem im Juli 2015 aufgetretenen Vorfall eines Informationslecks bei Registranten von Ashley Madison verwendet wurde. Es scheint jedoch, dass MD5 (und es war auch bekannt, dass es 26 Buchstaben des unteren Alphabets waren) tödlich war, und es scheint, dass bcrypt selbst für den Angreifer immer noch eine schmerzhafte Existenz ist.
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.
** Grundlegende Verwendung von bcrypt **
--bcrypt.gensalt (Runden = 12, Präfix = b'2b ') # Salz erzeugen --bcrypt.hashpw (Passwort, Salt) # Hash Passwort --bcrypt.checkpw (Passwort, Hash-Passwort) # Passwort überprüfen
Hash das Passwort wie folgt:
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'
Es hat die folgende Struktur.
$(2-stellige Version)$(2-stellige Arbeit-factor)$(Salz mit 22 Zeichen)(31 Zeichen Hash)
** Über das Argument von bcrypt.gensalt () **
bcrypt.gensalt () generiert für jeden Aufruf ein anderes Salt. Sie können beim Aufruf auch zwei Argumente angeben.
Derzeit scheint "2a" der Mainstream zu sein.
Im Folgenden sehen wir uns die Berechnungszeit an, wenn der Arbeitsfaktorwert von bcrypt.gensalt () geändert wird.
** Ausführungsumgebung: **
** Code und Ergebnisse **
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
Es überrascht nicht, dass jedes Inkrement des Arbeitsfaktors die Berechnungszeit verdoppelte.
** Ausführungsumgebung **
** SHA256 (+ Salz) Code **
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')
Ausführungsergebnis
> 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
** Verschlüsselungscode ** Das Ausführungsergebnis entspricht dem SHA256-Code.
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())
** Wörterbuch-Angriffscode **
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 'Schlagen! Das Passwort ist{}ist.'.format(p)
else:
return 'aus(´ ・ ω ・ `)'
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))
Ergebnis
sha256
user00:aus(´ ・ ω ・ `)
user01:aus(´ ・ ω ・ `)
user02:aus(´ ・ ω ・ `)
user03:aus(´ ・ ω ・ `)
user04:aus(´ ・ ω ・ `)
user05:aus(´ ・ ω ・ `)
user06:aus(´ ・ ω ・ `)
user07:aus(´ ・ ω ・ `)
user08:aus(´ ・ ω ・ `)
user09:Schlagen! Das Passwort lautet passw0rd.
Total time: 0.005 s
bcrypt-5
Total time: 0.715 s
bcrypt-12
Total time: 90.040 s
Sie können sehen, dass Wörterbuchangriffe auf bcrypt (und natürlich Brute Force-Angriffe) viel länger dauern als SHA256. Die tatsächliche Anzahl von Wörterbüchern und Benutzern ist größer, daher sollte es effektiv sein, Angreifer zu belästigen.
** 1. Wenn Sie bcrypt aus Python verwenden, verwenden Sie das bcrypt-Modul **
** 2. Wählen Sie den richtigen Arbeitsfaktor **
Durch Festlegen eines großen Werts für den Arbeitsfaktor kann Zeit für den Offline-Angriff eines Angreifers gespart werden. Dies wirkt sich jedoch auch auf die Leistung während des normalen Betriebs aus. Es ist ein Kompromiss zwischen Sicherheit und Komfort.
Bestimmen Sie die Anzahl der Iterationen, bei denen der Server das Kennwort innerhalb der gewünschten Zeit (10, 200 ms usw.) überprüft und verwendet.
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.
Ende.
Recommended Posts