Ho una lista di stringhe di cui ho bisogno per calcolare l'hash di, ma non riesco a capire come farlo in un modo che sarebbe resistente agli attacchi di collisione.
Ad esempio, in questo codice python:
def list_digest_1(strings):
import hashlib
hash = hashlib.sha1()
for s in strings:
hash.update(s)
return hash.hexdigest()
C'è una collisione tra [b'foo', b'bar']
e [b'foobar']
.
Questo può essere ridotto inserendo un separatore tra le stringhe:
def list_digest_2(strings):
import hashlib
hash = hashlib.sha1()
for s in strings:
hash.update(s)
hash.update(b'def list_digest_3(strings):
import hashlib
hash = hashlib.sha1()
for s in strings:
hash.update(
hashlib.sha1(s).digest()
)
return hash.hexdigest()
')
return hash.hexdigest()
Tuttavia, è ancora possibile creare facilmente una collisione iniettando i caratteri separatori nella stringa, ad es. [b'foo
e %code% . Questo potrebbe potenzialmente essere evitato sanitizzando le stringhe o altrimenti sfuggendo al carattere separatore, ma preferirei farlo se possibile. [b'foo', b'bar%code%baz']
bar', b'baz']
Un'altra possibilità è quella di separare ciascuna stringa separatamente, quindi combinare gli hash:
def rand_str(length):
return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(length)).encode('utf-8')
def rand_list(length, str_length):
return [rand_str(length=str_length) for _ in range(length)]
import tqdm
str_list = [rand_list(length=10000, str_length=2) for _ in tqdm.tqdm(range(1000))]
for hash_fun in list_digest_1, list_digest_2, list_digest_3:
t = timeit.Timer(lambda: [hash_fun(s) for s in str_list])
print('{}: {}'.format(hash_fun.__name__, t.timeit(number=1)))
# list_digest_1: 1.318927247000829
# list_digest_2: 2.4033974090016272
# list_digest_3: 7.667939508999552
Tieni presente che non sono sicuro che questo risolva il problema o lo faccia un passo indietro.
Non sto utilizzando l'hash per un'attività sensibile alla sicurezza, lo sto solo utilizzando come filtro preliminare per alcune query del database, per ridurre il risultato in termini di prestazioni direttamente dal test di uguaglianza ogni volta. Preferirei usare qualcosa che sia resistente a questo tipo di attacco (in teoria un attaccante potrebbe indurre artificialmente un carico extra presentando collisioni o qualcosa del genere) ma la terza versione peggiora significativamente quando ci sono un sacco di piccole stringhe, limitando i motivi di prestazioni per l'utilizzo di una funzione di hash in primo luogo.
def list_digest_1(strings):
import hashlib
hash = hashlib.sha1()
for s in strings:
hash.update(s)
return hash.hexdigest()
Come posso evitare questo problema nel calcolo dell'hash di un elenco di stringhe? Inoltre, se ci fosse uno strumento Python che dovrei usare per questo, sarei felice di saperlo.