Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Ticket #1824 (closed defect: fixed)

Opened 11 years ago

Last modified 10 years ago

Improve password security in quickstarted projects

Reported by: mramm Owned by: Gustavo
Priority: high Milestone: 2.0b1
Component: Quickstart Templates Version: trunk
Severity: normal Keywords:
Cc:

Description

Ben Bangert suggests that we can significantly improve the security of our password hashing algorithm:

"if there's only one salt, it just means you have to generate one massive set of rainbow tables to brute-force everyone's password"

So he suggests:

"this is how I crypt in one of my other apps, I have a crypt_password which does:

  password_salt = sha.new(os.urandom(60)).hexdigest()
  return password_salt + sha.new(password + password_salt).hexdigest()

then to verify it:

    crypt_pass = sha.new(password + self.password[:40]).hexdigest()
    if self.password[40:] == crypt_pass:
        return True
    else:
        return False

Change History

comment:1 Changed 11 years ago by mramm

  • Milestone changed from 2.0-preview-1 to 2.0-preview-2

comment:2 Changed 11 years ago by kless

Any recommendations to create a salt:

  1. Use a random string. So, do not use a hash function to do that.

In addition, a hash function (as sha) is not going to be faster than os.random

  1. It must be unique for each field
  • Here is the code that I'd use to ceate a different salt to each field:
    import random
    import string
    
    PRINT_CHARS = (string.digits + string.letters + string.punctuation)
    salt = ''.join(random.choice(PRINT_CHARS) for c in range(10))
    

It's very interesting to read the next Django's ticket, where they're solving the same problem:

 http://code.djangoproject.com/ticket/5600

comment:3 Changed 11 years ago by kless

Respect to security, the best is not create anything new and to use schemas already working.

Linux and *BSD use this schema to store the hashed passwords, using a $ as separator:

Algorithm identifier, the salt, the hash output

$1$a75vlqG6$km/45jM2tr.G5qaJPYH.x0

  1. The first field is the hash algorithm identifier:
    • $1$ is for MD5 used by Linux and any BSDs
    • $2a$ is for bcrypt used by OpenBSD

Folowing that, we could to have:

  • $3a$ for sha1
  • $3b$ for sha224
  • $3c$ for sha256
  • $3d$ for sha384
  • $3e$ for sha512
  1. To use a salt of 128 bits (16 bytes) -size used in bcrypt-
  1. As $ is used as separator, we would have to delete this character of SALT_CHARS.
  1. The maximum size of a hash, calculated of a 512 bits hash, is of 128 bytes

512 / 8 = 64 bytes x 2 (because hexa. chars are of 2by)

So, the size for the password field would be of a 150 characters. But could use anything bigger, as of 160.

comment:4 Changed 11 years ago by mramm

  • Milestone changed from 2.0-preview-2 to 2.0-preview-3

comment:5 Changed 10 years ago by Gustavo

  • Summary changed from Improve password seccurity on tgrepozewho to Improve password security in quickstarted projects
  • Component changed from Identity to Quickstart Templates
  • Milestone changed from 1.9.7a4 to 1.9.7b1

Just to mention that the authenticator plugin for repoze.who provided by tgext.authorization (and its predecessor, tg.ext.repoze.who) relies on the method {tg_package}.model.User.verify_password(), so such a solution should be implemented in that method.

comment:6 Changed 10 years ago by mramm

  • Priority changed from normal to high
  • Milestone changed from 1.9.7b1 to 2.0b1

Since this is implemented in user code, we just need an 80% solution, and not something that's maximally flexible/secure. So after all this, I think the simple salt solution suggested by Ben is better than kless's full solution for the standard template.

Though it would be cool to have a template or a recipe for the more feature-rich version.

comment:7 Changed 10 years ago by Gustavo

  • Owner changed from anonymous to Gustavo
  • Status changed from new to assigned

I'll take care of this in the sprint :)

comment:8 Changed 10 years ago by kless

Ago 2 days I started to create a python wrapper to BCrypt, which is currently used as the default password storage hash in OpenBSD, widely regarded as the most secure operating system available.

Resuming, that algorithm adds a salt to the hash generated, and the most important is that it allows you to increase the amount of work required to hash a password as computers get faster. Old passwords will still work fine, but new passwords can keep up with the times. If you want more information about BCrypt, read here:  http://www.usenix.org/events/usenix99/provos.html

The wrapper it's already functional:

In [1]: import pyx_bcrypt

In [2]: bcrypt = pyx_bcrypt.Engine()

In [3]: bcrypt.hash_key('crack my pass')
Out[3]: '$2a$10$5oEG2LCiivMMeceM1OjUHuZMQx/Hh39u/OSNVOn0TePjClZT.RoD2'

In [4]: bcrypt.hash_key('crack my pass if you can', 12)
Out[4]: '$2a$12$tf99GrfyJYUiLATIs6HnEeWwwi4mCl9BMErvrCKNty9Rdi2H.pgOi'

I'm supposed that will be full ready and uploaded to PyPi in the next week.

Note that it's necessary a working compiler.

comment:9 Changed 10 years ago by Gustavo

  • Status changed from assigned to closed
  • Resolution set to fixed

Fixed in [5892]

Note: See TracTickets for help on using tickets.