web-dev-qa-db-ja.com

フラスコ-bcrypt-ValueError:無効なソルト

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()])
8
Ali Faki

基本的に、ハッシュの前にデータをエンコードします:password.encode( 'utf-8')。 Unicodeとして提供される場合、エラーが発生する可能性があります。こちらもご覧ください: https://github.com/maxcountryman/flask-bcrypt/issues/9

3
user5732545

私の問題は@tomClarkによって説明されたものと似ています

私はPostgresをDDBBとして使用し、彼のドライバー、またはDDBBシステムは、常にをエンコードしますすでにエンコードされた文字列。 2番目のエンコードプロセスは、次のような無効なハッシュを作成します。

'\\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
11
J. Mulet

パスワードのハッシュ中に何かがうまくいかなかった場合にも、この例外が返されるようです。

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を設定しても、問題が解決しないことが判明しました。結局、nginxuWSGIに切り替えました。

9
Samuel Jaeschke

私の場合、問題はパスワードの保存中に行われる型変換に関連していました。 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)

確かなことはわかりませんが、本番環境やデータベースが異なれば、この型変換の処理も異なる可能性があることをお勧めします(場合によっては、逆に処理される場合もあれば、そうでない場合もあります)。

これは、入力文字列を制約付き文字セット(以前のソリューション)にエンコードすることが役立つ場合とそうでない場合がある理由も説明しています-型変換が機能する場合は、データベースから正しいハッシュを回復します比較。

とにかく、それは私にとってこの問題を解決しました。

7
thclark

.decode('utf-8')self.passwordに適用する必要があります。

def set_password(self, password):
    """Set password."""
    self.password = bcrypt.generate_password_hash(password).decode('utf-8')
4
Bob Jordan

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

4

私も同様の問題を抱えていました。パスワードを確認するための私のコードは次のとおりです。

if check_password_hash(form.password.data, user.pw_hashed):

順序を逆にしたとき:

if check_password_hash(user.pw_hashed, form.password.data):

それはうまくいきました。

3

私も同じ問題を抱えていました。私がチェックしようとしていたユーザー名とパスワードの組み合わせは、そもそもハッシュされていないことが判明しました。確認しようとしているユーザー名のパスワードが、プレーンテキストではなくハッシュ化されていることを確認してください。パスワードがハッシュされていないプレーンテキストで保存されている場合、このエラーが発生します。

1
Mahmoud Tokura

同様の問題が発生しました-次のようになりました:ValueError:無効なソルト-私のモデルでは、列の文字数が少なすぎることが判明しました:

password = Column(String(20))

私のデータベースとモデルでは、次のように変更する必要がありました。

password = Column(String(100))

そしてそれはうまくいった。

1
daga

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'))
0
Nikolay Fominyh