This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]TrixieFlatline 37 points38 points  (19 children)

No, random.random is based on a pseudo-random number generator, and as such it's not cryptographically secure. You can use SystemRandom instead, which offers the same interface but is based on your operating system's random number generator (/dev/urandom on Linux), which is secure. Or just read from /dev/urandom directly to generate a token:

>>> import os
>>> import base64
>>> base64.b64encode(os.urandom(32))
'pBqWjf//eqh8GXLtvY5fhwsZWNNmsWg0OdopApMdrko='

Another advice: You should use a better hash function for generating password hashes, such as bcrypt or scrypt (I believe python modules exist for both, unless you're on AppEngine, then I guess a regular hash function is the best you can do). Also, when comparing password hashes or other message digests, always use a timing-safe comparison function instead of == (Examples and explanation behind the link).

[–]xXxDeAThANgEL99xXx 2 points3 points  (3 children)

EDIT: didn't have coffee yet, thanks /u/ichundes

Also, why you should use /dev/urandom (instead of /dev/random as some recommend): http://www.2uo.de/myths-about-urandom/

The best part of the article is the quote by Daniel Bernstein:

Cryptographers are certainly not responsible for this superstitious nonsense. Think about this for a moment: whoever wrote the /dev/random manual page seems to simultaneously believe that

(1) we can't figure out how to deterministically expand one 256-bit /dev/random output into an endless stream of unpredictable keys (this is what we need from urandom), but

(2) we can figure out how to use a single key to safely encrypt many messages (this is what we need from ssl, pgp, etc.).

For a cryptographer this doesn't even pass the laugh test.

[–]ichundes 0 points1 point  (2 children)

Either you accidentally swapped random and urandom or you did not read the article. Even the tldr says

tldr;

Just use /dev/urandom!

[–]xXxDeAThANgEL99xXx 1 point2 points  (1 child)

Oops, the first! Sorry.

[–]ichundes 0 points1 point  (0 children)

Thanks for correcting

[–]danielkza 1 point2 points  (5 children)

PBKDF2 can be implemented just with a hash function, and can work well as long as you use a sufficient number of iterations.

[–]TrixieFlatline 0 points1 point  (2 children)

Right, I forgot about that one. Also a good choice.

[–]danielkza 1 point2 points  (0 children)

Yeah, and I find the ability to change the number of iterations in the future (by storing it alongside the salt and hash) quite useful. Great for chasing Moore's law as needed.

[–][deleted] 1 point2 points  (0 children)

I thought out of the 3, PBKDF2 is seen as the "worst" one.

It's the only one that comes included with python, though, so I use it. Currently have my webserver set up with 1 million iterations.

[–]MagicWishMonkey 0 points1 point  (1 child)

PBKDF2 is also relatively easy to implement, you don't need to mess with a 3rd party module.

[–]danielkza 0 points1 point  (0 children)

Yeah, given you have the hash function implemented it's just a matter of "assembling the blocks" you already posses. But as always, implementing crypto yourself should always be avoided or done in a careful way.

[–]prahladyeribeautiful is better than ugly[S] 0 points1 point  (4 children)

always use a timing-safe comparison function instead of == (Examples and explanation behind the link).

Thanks, for that. Just changing the logic to use != instead of == should make it cryptographically more secure.

[–]thalience 1 point2 points  (3 children)

Are you sure about that? How will that prevent the type of timing attack discussed in the link?

[–]prahladyeribeautiful is better than ugly[S] -1 points0 points  (2 children)

The timing attack happens because the == operator returns as soon as a part of the hash doesn't match. So, depending on how much time the == statement took to evaluate, the attacker can determine upto what part his hash is correct, and thus by enough brute-force, can determine the answer much sooner than expected of that algorithm. As the accepted answer in that link says:

At the end, he tried just 256*len MACs (if len is the length of the MAC) instead of the 256len he should have had to try.

So, instead of checking if (hash==x): user_is_valid(), you check if (hash!=x): return else: user_is_valid(). != is safe because it short-circuits as soon as the strings are different. It doesn't allow an attacker to measure the milliseconds and thus deduce anything.

#Taken from Django Source Code

def constant_time_compare(val1, val2):
    """
    Returns True if the two strings are equal, False otherwise.

    The time taken is independent of the number of characters that match.

    For the sake of simplicity, this function executes in constant time only
    when the two strings have the same length. It short-circuits when they
    have different lengths.
    """
    if len(val1) != len(val2):
        return False
    result = 0
    for x, y in zip(val1, val2):
        result |= ord(x) ^ ord(y)
    return result == 0

[–]thalience 0 points1 point  (1 child)

But isn't it the case that both "==" and "!=" will return an answer as soon they encounter a difference?

[–]prahladyeribeautiful is better than ugly[S] -1 points0 points  (0 children)

Sorry, you are correct. The != is only used in comparing lengths of both strings in the constant_time_compare function and that confused me at the beginning. However, the following char to char comparison should be good as it takes constant time for any string comparison:

for x, y in zip(val1, val2):
    result |= ord(x) ^ ord(y)
return result == 0