【问题标题】:Bcrypt Hash Returns TypeError("Unicode-objects must be encoded before hashing") and Invalid SaltBcrypt Hash Returns TypeError("Unicode-objects must be encrypted before hashing") and Invalid Salt
【发布时间】:2018-12-05 18:00:31
【问题描述】:

我查看了与此相关的所有 StackOverflow 问题,但我似乎无法弄清楚这一点。当我对密码进行哈希处理并对其自身进行检查时,它会使用当前代码返回 TypeError "Unicode-objects must be encrypted before hashing":

from scripts import tabledef
from flask import session
from sqlalchemy.orm import sessionmaker
from contextlib import contextmanager
import bcrypt

(Unrelated Python code...)

def hash_password(password):
     return bcrypt.hashpw(password.encode('utf8'), bcrypt.gensalt())


def credentials_valid(username, password):
    with session_scope() as s:
        user = s.query(tabledef.User).filter(
            tabledef.User.username.in_([username])).first()
        if user:

            return bcrypt.checkpw(password.encode('utf8'), user.password)
        else:
            return False

当我尝试通过设置 user.password= user.password.encode('utf8') 来修复此错误时,我得到“无效盐”。

这段代码有什么问题?

更新: 我通过用户的 Flask 输入存储密码:

import json
import sys
import os
import plotly
import pandas as pd
import numpy as np
import plotly.graph_objs as go


from scripts import tabledef
from scripts import forms
from scripts import helpers
from flask import Flask, redirect, url_for, render_template, request, session, flash, Markup
from flask_socketio import SocketIO, emit

@app.route('/', methods=['GET', 'POST'])
def login():
    if not session.get('logged_in'):
        form = forms.LoginForm(request.form)
        if request.method == 'POST':
            username = request.form['username'].lower()
            password = request.form['password']
            if form.validate():
                if helpers.credentials_valid(username, password):
                    session['logged_in'] = True
                    session['username'] = username
                    session['email'] = request.form['email']
                    session['password'] = request.form['password']
                    return json.dumps({'status': 'Login successful'})
                return json.dumps({'status': 'Invalid user/pass'})
            return json.dumps({'status': 'Both fields required'})
        return render_template('login.html', form=form)
    user = helpers.get_user()
    return render_template('home.html', user=user)

@app.route('/signup', methods=['GET', 'POST'])
def signup():
    if not session.get('logged_in'):
        form = forms.LoginForm(request.form)
        if request.method == 'POST':
            username = request.form['username'].lower()
            password = helpers.hash_password(request.form['password'])
            email = request.form['email']
            if form.validate():
                if not helpers.username_taken(username):
                    helpers.add_user(username, password, email)
                    session['logged_in'] = True
                    session['username'] = username
                    session['email'] = request.form['email']
                    session['password'] = request.form['password']
                    return json.dumps({'status': 'Signup successful'})
                return json.dumps({'status': 'Username taken'})
            return json.dumps({'status': 'User/Pass required'})
        return render_template('login.html', form=form)
    return redirect(url_for('login'))

这是我得到的错误:

/lib/python3.5/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/env/lib/python3.5/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/env/lib/python3.5/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/env/lib/python3.5/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/Flaskex-master/app.py", line 34, in login
    if helpers.credentials_valid(username, password):
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/Flaskex-master/scripts/helpers.py", line 64, in credentials_valid
    return bcrypt.checkpw(password.encode('utf8'), user.password)
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/env/lib/python3.5/site-packages/bcrypt/__init__.py", line 101, in checkpw
    raise TypeError("Unicode-objects must be encoded before checking")
TypeError: Unicode-objects must be encoded before checking

【问题讨论】:

  • 第一个函数应该有一个用于返回的选项卡。打错字了,抱歉。
  • 请发布完整的异常,而不仅仅是描述,并提供可运行的示例,以便我们自己重现错误。有关更多信息,请参阅帮助中的minimal reproducible example,但如果您不执行这两个中的至少一个,我们必须猜测哪一行可能会产生该错误。
  • 另外,您没有向我们展示您的商店user.password。但事实上它不是bytes(因为否则你尝试的encode 会引发AttributeError)意味着它显然不是从hash_password 返回的结果,所以......它是什么?
  • 您是否有机会将实际密码存储为user.password,而不是它的bcrypt?如果是这样:你永远不想这样做;使用 bcrypt(或任何密码散列器)的全部意义在于,您不需要将实际用户密码保存在可能泄露的地方。
  • 我添加了一些编辑来展示我如何存储密码。我还包括了我收到的错误。但是我将密码作为哈希值存储到数据库中。如果有帮助,密码的数据库类型是简单的 Column(String())。

标签: python flask bcrypt salt


【解决方案1】:

问题是您从 SQLAlchemy String 列中获取值并将其传递给 bcrypt.checkpwString 用于 Unicode 字符串,它提供的值为 str。但是bcrypt 仅适用于字节字符串,因此它需要bytes。这就是 TypeError 所说的“Unicode 对象必须在散列之前进行编码”告诉你的内容。

根据您使用的数据库后端和 DB-API 库(以及,对于某些后端,取决于您的数据库的配置方式),当您将 bytess 保存到 String 列时,它可能会保存s.decode(),在这种情况下,您可以只使用user.password.encode() 来获取相同的字节——但它可能不会。例如,它也可以只保存str(s)。在这种情况下,如果哈希是 bytesb'abcd',则列值将是字符串 "b'abcd'",因此调用 encode 会得到 b"b'abcd'",而不是 b'abcd'

处理此问题的最简洁方法是使用Binary1——或者,也许更好的是,Binary(60)2——来存储您的哈希值,而不是String 列。任何支持 Binary 的 DB-API 都将按原样存储 bytes,并将其作为 bytes 返回,这正是您想要的。


1。 Binary 是可选类型。如果您的 DB-ABI 不存在,则可以使用 BINARY 相同的类型。如果没有,请查看the list of types 并尝试从_Binary 继承的其他类型。名称或首字母缩写词中没有“大”的那些可能会更有效,但除此之外它们中的任何一个都应该工作。

2。在默认设置下,bcrypt 可打印摘要将始终为 60 个字节。数据库通常可以更紧凑地存储像BINARY(60) 这样的固定宽度字段,并且比像VARBINARY 这样的可变宽度字段更快地搜索它们。只使用普通的BINARY 可能没问题,但它也可能像VARBINARY 一样工作,或者它可能会浪费空间并像BINARY(255) 一样工作。

【讨论】:

  • 我遇到了同样的问题 - 编码错误导致 salt 错误。很奇怪,因为我完全遵循了文档,并且在非常基本的用例之上没有自定义。我对 bcrypt 和 sqlAlchemy 感到恼火,因为我遇到了这个错误,不得不用这样一个基本的用例将我的 DB 列更改为二进制。我很感谢这个问题和答案!否则很难调试。
猜你喜欢
  • 1970-01-01
  • 2018-12-23
  • 1970-01-01
  • 2016-11-13
  • 2020-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多