我正在使用Flask和flask-Bcrypt完成一个简单的用户登录.但是,当尝试使用存储在我的数据库中的用户登录时,我不断收到此错误
ValueError: Invalid salt
models.py
class User(db.Model): __tablename__ = "users" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) email = db.Column(db.String, nullable=False) password = db.Column(db.String, nullable=False) posts = db.relationship("Post", backref="author", lazy="dynamic") def __init__(self, name, email, password): self.name = name self.email = email self.password = bcrypt.generate_password_hash(password) def __repr__(self): return ''.format(self.name)
views.py
@app.route("/login", methods=["GET", "POST"]) def login(): form = LoginForm() if form.validate_on_submit(): user = User.query.filter(User.name == form.username.data).first() if user and bcrypt.check_password_hash(user.password, form.password.data): flash("you were just logged in!") login_user(user) return redirect(url_for("home")) else: flash("bad username or password") return render_template("login.html", form=form)
forms.py
class LoginForm(Form): username = StringField('username', validators=[DataRequired()]) password = PasswordField('password', validators=[DataRequired()])
小智.. 12
我的问题类似于@tomClark所描述的
我用的Postgres作为我DDBB和他的司机,或DDBB系统,编码总是一个已编码的字符串.第二个编码过程创建一个无效的哈希,如下所示:
'\\x24326224313224483352757749766438764134333757365142464f4f4f464959664d66673575??467873754e466250716f3166375753696955556b2e36'
正确的哈希看起来像这样:
$2b$12$Wh/sgyuhro5ofqy2.5znc.35AjHwTTZzabz.uUOya8ChDpdwvROnm
要解决它,我首先将哈希解码为utf8,而不是将其保存到DDBB.
示例代码:
def set_password(self, pw): pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt()) self.password_hash = pwhash.decode('utf8') # decode the hash to prevent is encoded twice
Samuel Jaesc.. 9
如果在散列密码时出现任何问题,似乎也会返回此异常.
从bcrypt
来源hashpw()
:
hashed = _bcrypt.ffi.new("unsigned char[]", 128) retval = _bcrypt.lib.crypt_rn(password, salt, hashed, len(hashed)) if not retval: raise ValueError("Invalid salt")
该bcrypt
(其中包Flask-Bcrypt
用来完成工作)返回ValueError: Invalid salt
时调用操作系统的bcrypt lib中返回一个错误.因此,如果由于某种原因它根本无法调用bcrypt lib,它仍将(错误地)返回Invalid salt
错误.
似乎是bcrypt
包实现中的一个缺陷- 它应该检查特定的值retval
.
在我的情况下,错误结果与Apache mod_wsgi
中的运行Flask有关virtualenv
.我可以直接运行flask没有问题(使用flask-cli
),但完全相同的应用程序实例bcrypt
在运行时不会成功使用mod_wsgi
.
通过修改我的Apache配置以使用virtualenv作为主要的Python环境来解决这个问题mod_wsgi
.
在httpd.conf
或/etc/httpd/conf.d/...
添加:
WSGIPythonHome /path/to/my/application-virtualenv
有关此配置的更多信息,请参见此处:虚拟环境 - mod_wsgi文档
我仍然怀疑我的特定问题与我的系统的python站点包或其他与python包含相关的东西有关.
编辑:设置WSGIPythonHome
结果不是解决问题.最后我用nginx切换到uWSGI.
我的问题类似于@tomClark所描述的
我用的Postgres作为我DDBB和他的司机,或DDBB系统,编码总是一个已编码的字符串.第二个编码过程创建一个无效的哈希,如下所示:
'\\x24326224313224483352757749766438764134333757365142464f4f4f464959664d66673575??467873754e466250716f3166375753696955556b2e36'
正确的哈希看起来像这样:
$2b$12$Wh/sgyuhro5ofqy2.5znc.35AjHwTTZzabz.uUOya8ChDpdwvROnm
要解决它,我首先将哈希解码为utf8,而不是将其保存到DDBB.
示例代码:
def set_password(self, pw): pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt()) self.password_hash = pwhash.decode('utf8') # decode the hash to prevent is encoded twice
如果在散列密码时出现任何问题,似乎也会返回此异常.
从bcrypt
来源hashpw()
:
hashed = _bcrypt.ffi.new("unsigned char[]", 128) retval = _bcrypt.lib.crypt_rn(password, salt, hashed, len(hashed)) if not retval: raise ValueError("Invalid salt")
该bcrypt
(其中包Flask-Bcrypt
用来完成工作)返回ValueError: Invalid salt
时调用操作系统的bcrypt lib中返回一个错误.因此,如果由于某种原因它根本无法调用bcrypt lib,它仍将(错误地)返回Invalid salt
错误.
似乎是bcrypt
包实现中的一个缺陷- 它应该检查特定的值retval
.
在我的情况下,错误结果与Apache mod_wsgi
中的运行Flask有关virtualenv
.我可以直接运行flask没有问题(使用flask-cli
),但完全相同的应用程序实例bcrypt
在运行时不会成功使用mod_wsgi
.
通过修改我的Apache配置以使用virtualenv作为主要的Python环境来解决这个问题mod_wsgi
.
在httpd.conf
或/etc/httpd/conf.d/...
添加:
WSGIPythonHome /path/to/my/application-virtualenv
有关此配置的更多信息,请参见此处:虚拟环境 - mod_wsgi文档
我仍然怀疑我的特定问题与我的系统的python站点包或其他与python包含相关的东西有关.
编辑:设置WSGIPythonHome
结果不是解决问题.最后我用nginx切换到uWSGI.
就我而言,问题与密码存储期间进行的类型转换有关.使用bcrypt.generate_password_hash(plaintext)
返回二进制值,如b'$2b$12$zf/TxXZ4JJZ/lFX/BWALaeo0M.wNXrQXLqIFjmZ0WebqfVo9NES56'
.
与我的一样,您的密码列设置为字符串:
password = db.Column(db.String, nullable=False)
我发现上面生成哈希值,将二进制值存储在我的字符串密码列中,然后只是检索它导致了由于SQLAlchemy的类型转换而产生的不同值 - 与bcrypt完全无关!
关于正确列类型的问题帮助我意识到,为了正确的往返,我必须将密码存储为二进制.尝试使用以下代码替换列定义:
password = db.Column(db.Binary(60), nullable=False)
我不确定,但建议不同的生产环境和数据库可以不同地处理这种类型的转换(在某些情况下可逆转,而不是在其他情况下),或许可以解释@Samuel Jaeschke所带来的混合成功.
这也解释了为什么将输入字符串编码为约束字符集(较早的解决方案)在某些情况下可能有所帮助而不是其他情况 - 如果它导致从/到类型转换工作,那么您将从数据库中恢复正确的散列比较.
无论如何,这为我解决了这个问题.