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 '<User {}>'.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()])
基本的に、ハッシュの前にデータをエンコードします:password.encode( 'utf-8')。 Unicodeとして提供される場合、エラーが発生する可能性があります。こちらもご覧ください: https://github.com/maxcountryman/flask-bcrypt/issues/9
私の問題は@tomClarkによって説明されたものと似ています
私はPostgresをDDBBとして使用し、彼のドライバー、またはDDBBシステムは、常にをエンコードしますすでにエンコードされた文字列。 2番目のエンコードプロセスは、次のような無効なハッシュを作成します。
'\\x24326224313224483352757749766438764134333757365142464f4f4f464959664d66673575467873754e466250716f3166375753696955556b2e36'
正しいハッシュは次のようになります。
$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
パスワードのハッシュ中に何かがうまくいかなかった場合にも、この例外が返されるようです。
hashpw()
のbcrypt
ソースから:
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
が作業を完了するために使用する)は、OSのbcryptlibへの呼び出しがエラーを返すたびにValueError: Invalid salt
を返します。したがって、何らかの理由でbcrypt libをまったく呼び出せない場合でも、(誤って)Invalid salt
エラーが返されます。
bcrypt
パッケージの実装に欠陥があるようです-retval
の特定の値をチェックする必要があります。
私の場合、エラーはvirtualenv
のApachemod_wsgi
でFlaskを実行していることに関連していることが判明しました。flask =問題なく直接(flask-cli
を使用)、ただしmod_wsgi
で実行すると、まったく同じアプリインスタンスがbcrypt
を正常に使用できません。
この問題は、virtualenvをメインのPython mod_wsgi
の環境として使用するようにApache構成を変更することで解決されました。
httpd.conf
または/etc/httpd/conf.d/...
の下に次を追加します。
WSGIPythonHome /path/to/my/application-virtualenv
この構成の詳細については、次を参照してください。 仮想環境— mod_wsgiドキュメント
私の特定の問題は、システムのpython site-packages、またはpython includes)に関連する何かに関連しているのではないかと思います。
編集: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)
確かなことはわかりませんが、本番環境やデータベースが異なれば、この型変換の処理も異なる可能性があることをお勧めします(場合によっては、逆に処理される場合もあれば、そうでない場合もあります)。
これは、入力文字列を制約付き文字セット(以前のソリューション)にエンコードすることが役立つ場合とそうでない場合がある理由も説明しています-型変換が機能する場合は、データベースから正しいハッシュを回復します比較。
とにかく、それは私にとってこの問題を解決しました。
.decode('utf-8')
をself.password
に適用する必要があります。
def set_password(self, password):
"""Set password."""
self.password = bcrypt.generate_password_hash(password).decode('utf-8')
python 3とbcrypt0.7.1を使用していると思います。最初にデータベース内のユーザーを削除してから、モデルに移動して.decode( 'utf-8')をに追加する必要があります。次のようなgenerate_password_hash()メソッド:
pw_hash = bcrypt.generate_password_hash(‘hunter2’).decode('utf-8')
または、flask-bcrypt == 0.7.1をアンインストールし、flask-bcrypt == 0.62をインストールすることもできます。フラスコをインストールする前に、必ずテーブルからユーザーを削除してください-bcrypt == 0.62
私も同様の問題を抱えていました。パスワードを確認するための私のコードは次のとおりです。
if check_password_hash(form.password.data, user.pw_hashed):
順序を逆にしたとき:
if check_password_hash(user.pw_hashed, form.password.data):
それはうまくいきました。
私も同じ問題を抱えていました。私がチェックしようとしていたユーザー名とパスワードの組み合わせは、そもそもハッシュされていないことが判明しました。確認しようとしているユーザー名のパスワードが、プレーンテキストではなくハッシュ化されていることを確認してください。パスワードがハッシュされていないプレーンテキストで保存されている場合、このエラーが発生します。
同様の問題が発生しました-次のようになりました:ValueError:無効なソルト-私のモデルでは、列の文字数が少なすぎることが判明しました:
password = Column(String(20))
私のデータベースとモデルでは、次のように変更する必要がありました。
password = Column(String(100))
そしてそれはうまくいった。
bcrypt
を使用するのにflask-bcrypt
はまったく必要ありません。
次のようなことをしてください。
class User(Base):
_password = db.Column("password", db.String, nullable=False)
@hybrid_property
def password(self):
return self._password
@password.setter
def password(self, value):
bvalue = bytes(value, 'utf-8')
temp_hash = bcrypt.hashpw(bvalue, bcrypt.gensalt())
self._password = temp_hash.decode('utf-8')
def check_password(self, value):
return bcrypt.checkpw(value.encode('utf-8'), self._password.encode('utf-8'))