Bcrypt Hash Returns TypeError("Unicode-objects must be encoded before hashing") 和无效盐
Bcrypt Hash Returns TypeError("Unicode-objects must be encoded before hashing") and Invalid Salt
我查看了所有与此相关的 Whosebug 问题,但我似乎无法弄清楚。当我对密码进行哈希处理并对照自身进行检查时,它 returns TypeError "Unicode-objects must be encoded 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')
修复此错误时,我得到 "Invalid Salt"。
这段代码有什么问题?
更新:
我通过用户输入的 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
问题是您从 SQLAlchemy String
列中获取值并将其传递给 bcrypt.checkpw
。 String
适用于 Unicode 字符串,它提供的值为 str
。但是 bcrypt
只适用于字节串,所以它需要一个 bytes
。这就是说 "Unicode-objects must be encoded before hashing" 的 TypeError
告诉你的。
取决于您使用的数据库后端和 DB-API 库(以及,对于某些后端,取决于您的数据库是如何配置的),当您保存 bytes
值 s
到 String
列,它可能会保存 s.decode()
,在这种情况下,您可以只使用 user.password.encode()
来取回相同的字节——但它可能不会。例如,它也可以只保存 str(s)
。在这种情况下,如果散列是 bytes
值 b'abcd'
,列值将是字符串 "b'abcd'"
,因此调用 encode
可以得到 b"b'abcd'"
, 而不是 b'abcd'
.
处理这个问题最干净的方法是使用 Binary
列1——或者,也许更好,Binary(60)
2——存储你的哈希值,而不是 String
列。任何支持 Binary
的 DB-API 将按原样存储 bytes
,而 return 将其存储为 bytes
,这正是您想要的。
1. Binary
是可选类型。如果您的 DB-ABI 不存在它,则可以使用与 BINARY
相同的类型。如果不是,请查看 the list of types 并尝试从 _Binary
继承的其他类型。名称或首字母缩略词中没有 "large" 的那些可能会更有效率,但除此之外,它们中的任何一个都应该有效。
2。使用默认设置,bcrypt
可打印摘要将始终恰好为 60 字节。数据库通常可以更紧凑地存储像 BINARY(60)
这样的固定宽度字段,并且比像 VARBINARY
这样的可变宽度字段更快速地搜索它们。只使用普通的 BINARY
可能没问题,但它也可能像 VARBINARY
那样工作,或者它可能会浪费 space 而像 BINARY(255)
一样工作,等等
我查看了所有与此相关的 Whosebug 问题,但我似乎无法弄清楚。当我对密码进行哈希处理并对照自身进行检查时,它 returns TypeError "Unicode-objects must be encoded 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')
修复此错误时,我得到 "Invalid Salt"。
这段代码有什么问题?
更新: 我通过用户输入的 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
问题是您从 SQLAlchemy String
列中获取值并将其传递给 bcrypt.checkpw
。 String
适用于 Unicode 字符串,它提供的值为 str
。但是 bcrypt
只适用于字节串,所以它需要一个 bytes
。这就是说 "Unicode-objects must be encoded before hashing" 的 TypeError
告诉你的。
取决于您使用的数据库后端和 DB-API 库(以及,对于某些后端,取决于您的数据库是如何配置的),当您保存 bytes
值 s
到 String
列,它可能会保存 s.decode()
,在这种情况下,您可以只使用 user.password.encode()
来取回相同的字节——但它可能不会。例如,它也可以只保存 str(s)
。在这种情况下,如果散列是 bytes
值 b'abcd'
,列值将是字符串 "b'abcd'"
,因此调用 encode
可以得到 b"b'abcd'"
, 而不是 b'abcd'
.
处理这个问题最干净的方法是使用 Binary
列1——或者,也许更好,Binary(60)
2——存储你的哈希值,而不是 String
列。任何支持 Binary
的 DB-API 将按原样存储 bytes
,而 return 将其存储为 bytes
,这正是您想要的。
1. Binary
是可选类型。如果您的 DB-ABI 不存在它,则可以使用与 BINARY
相同的类型。如果不是,请查看 the list of types 并尝试从 _Binary
继承的其他类型。名称或首字母缩略词中没有 "large" 的那些可能会更有效率,但除此之外,它们中的任何一个都应该有效。
2。使用默认设置,bcrypt
可打印摘要将始终恰好为 60 字节。数据库通常可以更紧凑地存储像 BINARY(60)
这样的固定宽度字段,并且比像 VARBINARY
这样的可变宽度字段更快速地搜索它们。只使用普通的 BINARY
可能没问题,但它也可能像 VARBINARY
那样工作,或者它可能会浪费 space 而像 BINARY(255)
一样工作,等等