GlobaLeaks: GlobaLeaks is vulnerable to timing attacks.

2016-12-29T18:01:24
ID H1:194647
Type hackerone
Reporter edoverflow
Modified 2017-02-26T09:37:07

Description

Dear GlobaLeaks bug bounty team,

GlobaLeaks is vulnerable to timing attacks, because the check_password() function performs a byte-by-byte comparison, which terminates early when two characters do not match.

Summary


Timing attacks are a type of side channel attack where one can discover valuable information by recording the time it takes for a cryptographic algorithm to execute.

👎 Don't use this:

~~~ def check_password(guessed_password, salt, password_hash): return hash_password(guessed_password, salt) == password_hash ~~~

The == operation does a byte-by-byte comparison of two values and as soon as the two differentiate it terminates. This means the longer it takes until the operation returns, the more correct characters the attacker has guessed.

Link to source code: https://github.com/globaleaks/GlobaLeaks/blob/master/backend/globaleaks/security.py#L327-L328

👍 Use this instead:

~~~ hmac.compare_digest(hash_password(guessed_password, salt), password_hash) ~~~

The compare_digest() function does not terminate as soon as two bytes are not the same.

On top of that, many password hashing modules come with a ready made time independent comparison function. The bcrypt package uses the checkpw() function.

~~~ import bcrypt

def bcrypt_password(): password = b"hunter2" hashed = bcrypt.hashpw(password, bcrypt.gensalt()) if bcrypt.checkpw(password, hashed): print("It matches! :)") else: print("It does not match :(") ~~~

In your case you are using the scrypt module so maybe you want follow the recommendations from the docs:

~~~ import scrypt

def verify_password(hashed_password, guessed_password, maxtime=0.5): try: scrypt.decrypt(hashed_password, guessed_password, maxtime) return True except scrypt.error: return False ~~~

Link to docs: https://pypi.python.org/pypi/scrypt/0.8.0

Yours sincerely, Ed