6

tl;dr -- How do I use a Python-side library such as PassLib to hash passwords before inserting them into a MySQL DB with SQLAlchemy?

Alright, so I've been banging my head on my desk for a day or two trying to figure this out, so here it goes:

I am writing a web application using Pyramid/SQLAlchemy and I'm trying to interface with my MySQL database's Users table.

Ultimately, I want to do something like the following:

Compare a password to the hash:

if user1.password == 'supersecret'

Insert a new password:

user2.password = 'supersecret'

I'd like to be able to use PassLib to hash my passwords before they go to the database, and I'm not really a fan of using the built-in MySQL SHA2 function since it's not salted.

However, just to try it, I do have this working using the SQL-side function:

from sqlalchemy import func, TypeDecorator, type_coerce
from sqlalchemy.dialects.mysql import CHAR, VARCHAR, INTEGER
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column

class SHA2Password(TypeDecorator):
  """Applies the SHA2 function to incoming passwords."""
  impl = CHAR(64)

  def bind_expression(self, bindvalue):
    return func.sha2(bindvalue, 256)

  class comparator_factory(CHAR.comparator_factory):
    def __eq__(self, other):
      local_pw = type_coerce(self.expr, CHAR)
      return local_pw == func.sha2(other, 256)

class User(Base):
  __tablename__ = 'Users'
  _id = Column('userID', INTEGER(unsigned=True), primary_key=True)
  username = Column(VARCHAR(length=64))
  password = Column(SHA2Password(length=64))

  def __init__(self, username, password):
    self.username = username
    self.password = password

This was copied from the example 2 at http://www.sqlalchemy.org/trac/wiki/UsageRecipes/DatabaseCrypt

So that works and allows me to use the built-in MySQL SHA2 function (by calling func.sha2()) and do exactly what I want. However, now I'm trying to replace this with PassLib on the Python side.

PassLib presents two functions: one to create a new password hash, and one to verify a password:

from passlib.hash import sha256_crypt

new_password = sha256_crypt.encrypt("supersecret")

sha256_crypt.verify("supersecret", new_password)

I can't quite figure out how to actually implement this. Having read all the documentation, I think it is either a different form of TypeDecorator, a custom type declaration, a hybrid value, or a hybrid property. I tried following this, but it doesn't really make sense to me nor does the code suggested there actually run.

So, to sum up my question -- how do I overload the = and == operators so that they run things through the appropriate hash functions?

2 Answers 2

6

PasswordType from sqlalchemy-utils should be the best fit for this issue. It uses passlib. Snipped from the docs:

The following usage will create a password column that will automatically hash new passwords as pbkdf2_sha512 but still compare passwords against pre-existing md5_crypt hashes. As passwords are compared; the password hash in the database will be updated to be pbkdf2_sha512.

class Model(Base):
    password = sa.Column(PasswordType(
        schemes=[
            'pbkdf2_sha512',
            'md5_crypt'
        ],
        deprecated=['md5_crypt']
    ))

Verifying password is as easy as:

target = Model()
target.password = 'b'
# '$5$rounds=80000$H.............'
target.password == 'b'
# True
2
  • Thanks! This is new since I asked this question, but is exactly what I was looking for at the time!
    – kc9jud
    Commented Oct 9, 2015 at 5:41
  • 1
    What about adding a salt column to this Model? Is there a way to use a salt with sqlalchemy-utils and store?
    – steve
    Commented Feb 6, 2016 at 6:59
3

As I understand it, what you want is this:

  1. Encrypt the user's password when creating the account. Use your salt and algorithm
  2. When the user logs in, hash the incoming password the same way you did when you stored it
  3. Compare the two hashes using regular string comparison in your db request

So, something like this for the login code:

from passlib.hash import sha256_crypt
passHash = sha256_crypt.encrypt(typed_password)
// call your sqlalchemy code to query the db with this value (below)

// In your SQLAlchemy code assuming "users" is your users table
// and "password" is your password field
s = users.select(and_(users.username == typed_username, users.password == passHash))
rs = s.execute()

rs would be the resultset of matching users (should be zero or one of course).

Disclaimer - I did not test any of this

Edit: Thank you for pointing out that PassLib uses a different salt each time it's run. Your best bet in that case, since there doesn't seem to be a straightforward way to do it with sqlalchemy, is the below:

s=users.select(users.username == typed_username)
rs = s.execute()
userRow = rs.fetchone()
if (sha256_crypt.verify(userRow.password)):
    # you have a match

Also, to address your request for abstracting: a common methodology for handling this operation is to create a "security" utility class for getting the user (object) that matches the passed login credentials.

The problem with your current setup is that the User constructor has two different operational goals that, though related, are not necessarily the same thing: authenticating a user and getting a User object (for, say, a list of users in a group). The constructor becomes needlessly complex in that case. It's better to put that logic where it can be encapsulated with other security or login-related functionality such as logging in a user via session ID or SSO token instead of username/password:

security.loginUser(username, password)
# or security.loginUser(single_sign_on_token), etc. for polymorphic Security
loggedInUser = security.getLoggedInUser()

... later ...
otherUser = User(username) #single job, simple, clean
4
  • I guess there's two reasons this isn't what I'm looking for. For one, PassLib never generates the same password hash string twice on account of the salting, so you can't just compare strings. You have to use the verify() function. Second, I'm trying to abstract away all of the hashing mechanics/logic into the class, so just running verify() on the object's password variable kind of defeats that purpose. Thanks for the suggestion, though!
    – kc9jud
    Commented Feb 21, 2013 at 0:13
  • Thanks for the info about PassLib. It took some digging to find in the documentation where it actually said that. I've updated to address those.
    – Fyrilin
    Commented Feb 21, 2013 at 5:12
  • I agree 100% -- I don't intend to verify credentials or do any auth in the Users object. Having some kind of token is definitely a good idea, however, I'd like to have the Users class abstract away the specifics of the hash function. I'd like it so that I could decide to use a different hash function provided in PassLib or another library and be able to switch by changing only the Users class -- or if I wanted to use the PostgreSQL hashing on PostgreSQL and PassLib on MySQL, etc. Basically, I want to hide the fact that the passwords are being hashed at all.
    – kc9jud
    Commented Feb 22, 2013 at 1:22
  • Just to clarify -- I don't want to use the PassLib functions within SQL. All of the hash functions happen in Python before any SQL gets constructed.
    – kc9jud
    Commented Feb 24, 2013 at 20:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.