maxcountryman / flask-bcrypt

Flask-Bcrypt is a Flask extension that provides bcrypt hashing utilities for your application.

Home Page:http://readthedocs.org/docs/flask-bcrypt/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

pw_hash needs to be encoded in check_password_hash()

maximebf opened this issue · comments

The password hash needs to be encoded like the password in the check_password_hash() method. As is, the 0.7.0 version raises an error when using a unicode password hash.
Python 2.7

Okay. Let's add a test case for this, since our current seems to have missed this. I can follow up in a little bit with a patch to fix and release it as 0.7.1.

Awesome. Thanks for such a quick answer :)

So far I can't reproduce your error. I set up a simple test, here's the diff:

diff --git a/test_bcrypt.py b/test_bcrypt.py
index e9bd52b..43480cf 100644
--- a/test_bcrypt.py
+++ b/test_bcrypt.py
@@ -1,3 +1,4 @@
+# coding:utf-8
 import unittest

 import flask
@@ -48,6 +49,13 @@ class BasicTestCase(unittest.TestCase):
     def test_rounds_set(self):
         self.assertEqual(self.bcrypt._log_rounds, 6)

+    def test_unicode(self):
+        password = u'東京'
+
+        h = generate_password_hash(password)
+
+        self.assertIs(check_password_hash(h, password), True)
+

 if __name__ == '__main__':
     unittest.main()

Maybe you could explain how you've got a Unicode password hash? I don't think Flask-Bcrypt is producing that.

The hash is stored as a unicode string in the db. Something like:

user = User()
user.password = generate_password_hash(password)
db.session.add(user)
# ...
user = User.query.get(1)
check_password_hash(user.password, password) # in this case, user.password is a unicode

I can just wrap it in str() but no encoding may break some existing apps.

Ah I see. Yeah Flask-Bcrypt can just look for this and convert. No need for you to do anything.

Cool, thanks!

So given what you've said above, I think the following test should fail. But it seems to be passing.

diff --git a/test_bcrypt.py b/test_bcrypt.py
index e9bd52b..56e5de1 100644
--- a/test_bcrypt.py
+++ b/test_bcrypt.py
@@ -1,3 +1,4 @@
+# coding:utf-8
 import unittest

 import flask
@@ -48,6 +49,11 @@ class BasicTestCase(unittest.TestCase):
     def test_rounds_set(self):
         self.assertEqual(self.bcrypt._log_rounds, 6)

+    def test_unicode_hash(self):
+        password = '東京'
+        h = generate_password_hash(password).decode('utf-8')
+        self.assertIs(check_password_hash(h, password), True)
+

 if __name__ == '__main__':
     unittest.main()

By the way, the reason this is working is because of this: https://github.com/maxcountryman/flask-bcrypt/blob/master/flask_bcrypt.py#L184-L185

Is it possible some other part of your application is actually causing the exception? Do you have a stacktrace you can share?

I noticed you encoded the password. The problem is pw_hash which cannot be a unicode according to https://github.com/pyca/bcrypt/blob/master/src/bcrypt/__init__.py#L56

Stack trace:

  File "/home/example/venv/local/lib/python2.7/site-packages/frasco_users/__init__.py", line 275, in check_password
    self.bcrypt.check_password_hash(getattr(user, pwcol), password)
  File "/home/example/venv/local/lib/python2.7/site-packages/flask_bcrypt.py", line 180, in check_password_hash
    return safe_str_cmp(bcrypt.hashpw(password, pw_hash), pw_hash)
  File "/home/example/venv/local/lib/python2.7/site-packages/bcrypt/__init__.py", line 57, in hashpw
    raise TypeError("Unicode-objects must be encoded before hashing")
TypeError: Unicode-objects must be encoded before hashing

Casting the hash to unicode doesn't help: I can't seem to reproduce this error at all unfortunately.

I'm not sure what else to do, I've tried all the combinations of encoded and decoded strings and unicode objects I can think of and they all work. I think something else might be going on here.

diff --git a/test_bcrypt.py b/test_bcrypt.py
index e9bd52b..8bf981e 100644
--- a/test_bcrypt.py
+++ b/test_bcrypt.py
@@ -1,3 +1,4 @@
+# coding:utf-8
 import unittest

 import flask
@@ -48,6 +49,11 @@ class BasicTestCase(unittest.TestCase):
     def test_rounds_set(self):
         self.assertEqual(self.bcrypt._log_rounds, 6)

+    def test_unicode_hash(self):
+        password = u'東京'
+        h = unicode(generate_password_hash(password))
+        self.assertIs(check_password_hash(h, password), True)
+

 if __name__ == '__main__':
     unittest.main()

Okay I think I know what's going on now: I had an old version of bcrypt installed on my system, so these tests were continuing to work when they should not have been.

I was going to say, the above test fails on my machine (with a clean install in a virtualenv).
I'm off to bed, will be back tomorrow :)

0.7.1 is now on PyPI. Sorry about the trouble.

Great. Thank you for your work.